viernes, 18 de octubre de 2013

Tarjetas de contacto en ASP.Net MVC

A veces nos perdemos en cuestiones totalmente estéticas a la hora de realizar webs para entornos móviles, donde términos como "responsive" acaparan todo el protagonismo, olvidando que, a veces, podemos dotar de funcionalidades básicas que diferencien estas webs de los entornos de escritorio ya que nuestras páginas se van a mostrar en dispositivos que disponen de más funcionalidad que un navegador web de un computador de sobremesa o portátil.

Vamos a ver como integrar nuestra web con las funcionalidades primordiales de los teléfonos, es decir, realizar llamadas y guardar contactos en la agenda.

Llamadas Telefónicas

La realización de llamadas telefónicas desde la web se basa en una técnica ya utilizada para abrir el cliente de correo desde el explorador. Si para enviar un email utilizamos el esquema mailto:, para indicar que un enlace corresponde a un teléfono, usaremos tel:

 <html>  
   <body>  
    <div>  
      <label>Telf:</label>  
      <a href="tel:96 000 00 00" class="tel">Llamar</a>  
    </div>  
   </body>  
 </html>  

Esto nos generará un enlace al número telefónico especificado.

Como curiosidad, ¿ que ocurrirá si abrimos el enlace en un dispositivo sin posibilidad de realizar llamadas telefónicas ?

El dispositivo detecta que es un número telefónico y nos da la opción de guardarlo en contactos.

Gestión de contactos

La otra posibilidad es la de ofrecer tarjetas de contacto completas desde la web. Podremos dar oportunidad a los visitantes de nuestra web de obtener nuestros datos de contacto en nuestro curriculum online público, los de los clientes asignados a un vendedor desde nuestro programa de gestión, o nuestra tarjeta de visita de nuestro restaurante

El mecanismo necesario para la integración de los contactos se basa en:

  • Tipo MIME para la tarjeta de contacto text/x-vcard
  • Formato VCARD

Veamos la implementación de una descarga en ASP.Net MVC

Utilizaremos un controlador sin vista asociada para devolver el contenido. El primer paso es anunciar el tipo MIME anteriormente citado y sugerir al navegador un nombre de archivo en la descarga

   
   Response.ContentType = "text/x-vcard";  
   Response.Headers.Add("Content-Disposition", String.Format(@"attachment; filename=""{0}.vcf""", "Contacto"));  
   

Ahora sólo nos falta generar el cuerpo de la respuesta. El formato VCard es un sencillo formato de texto, por lo que para generarlo construiremos una cadena y la devolveremos con el ActionResult Content, que nos permite devolver un valor de texto como cuerpo del mensaje.

   
 String result = String.Format(@"BEGIN:VCARD  
VERSION:3.0  
ORG:{0}  
TEL;TYPE=WORK:{1}  
EMAIL;TYPE=WORK:{2}  
ADR;TYPE=WORK:;;{3};{4};;{5};{6}  
END:VCARD", "Nombre", "96 000 00 00", "email@email.com", "Direccion", "Ciudad","46000", "ES");  
   
       return Content(result);  

Es importante comentar que las líneas de la VCard han de estar alineadas a la izquierda sin espacios en blanco, ya que si no el dispositivo nos puede dar un error de formato incorrecto

El código completo de la acción es el siguiente:

 public ActionResult VCard()  
 {  
   
 Response.ContentType = "text/x-vcard";   
 Response.Headers.Add("Content-Disposition", String.Format(@"attachment; filename=""{0}.vcf""", "Contacto"));   
   
 String result = String.Format(@"BEGIN:VCARD
VERSION:3.0
ORG:{0}
TEL;TYPE=WORK:{1}
EMAIL;TYPE=WORK:{2}
ADR;TYPE=WORK:;;{3};{4};;{5};{6}
END:VCARD", "Nombre", "96 000 00 00", "email@email.com", "Direccion", "Ciudad","46000", "ES");   
     
     return Content(result);   
   
 }  

Si accedemos a la descarga, se procederá a guardar el archivo en el dispositivo y su posterior importación a la agenda de contactos.

Esto es todo, hemos visto como generar acciones telefónicas para efectuar llamadas o añadir contactos a la agenda del dispositivo, utilizando las siguientes técnicas:

  • Esquema tel: en la dirección de un enlace.
  • Uso de Content-Type: text/x-vcard.
  • Uso de Content como ActionResult para devolver un contenido de tipo System.String.
  • Formato vCard.

jueves, 10 de octubre de 2013

Html Helpers en MVC con contenido

Los Helpers de HTML en MVC nos facilitan la creación de vistas al encapsular la creación del código de marcado. Desarrollar un Helper que cree un label o un input, es un caso trivial, pero podemos encontrarnos con que necesitemos utilizar la sintaxis de Razor para generar contenido entre la etiqueta de apertura y cierre, por ejemplo llamar a vistas parciales u obtener el resultado de pintar un Action

Como hemos comentado, generar un Helper para un input o un label es una tarea sencilla, con un método de extensión podemos resolver nuestro requerimiento:

 public static MvcHtmlString MiLabel(this HtmlHelper htmlHelper, String NombreCampo, String Caption)  
 {  
   String formatresult = "<label for='{0}'>{1}</label>";  
   return new MvcHtmlString(String.Format(formatresult, NombreCampo, Caption));  
 }  

Una llamada a este helper:

 @MiLabel("NombreCampo", "Valor")  

Nos generaría en el marcado resultante:

 <label for="NombreCampo">Valor</label>  

El asunto se complica si necesitamos marcado entre las etiquetas de apertura y cierre. Un enfoque para solucionar esto sería escribir los métodos correspondientes al inicio y al fin de la etiqueta, y llamarlos de la misma forma que en el ejemplo anterior.

PERO, somos personas curiosas y elegantes, por lo que decidimos que lo queremos hacer bien, de forma concisa. Nuestra curiosidad nos lleva a observar que todas las vistas de creación y edición que nos genera Visual Studio hay un elegante using que rodea entre llaves todo el marcado de los campos del formulario.

@using (Html.BeginForm("Accion", "Controlador", FormMethod.Post)) {    
{  
           <h3>Titulo</h3>  
}  

¡Un momento! ¿ using ? ¿ No se utiliza la clausula using para crear un objeto que implementa la interfaz IDisposable, ejecutar código y destruirlo de forma ordenada ?

Efectivamente esa es la forma con la que podremos crear nuestros helpers, sólo necesitaremos:

  • Una clase que implemente la interfaz IDisposable y
  • un método que nos devuelva un objeto de esta clase

En resumidas cuentas, utilizaremos el método para crear un objeto y general el HTML del inicio del elemento, y el método Dispose del objeto creado para cerrarlo

Sin más dilación, vamos a ver un ejemplo de esta técnica

Creamos un método de extensión que devuelva una instancia de la clase que creemos después:

     public static MvcContainer BeginContainer(this HtmlHelper htmlHelper, String tagname)  
     {  
       return new MvcContainer(htmlHelper.ViewContext.Writer, tagname);  
     }  

Y veamos la definición de la clase MvcContainer generada en el paso anterior:

 public class MvcContainer : IDisposable  
   {  
     private bool disposed;  
     private readonly TextWriter _writer;  
     private string _tagname;  
     public string tagname  
     {  
       get { return _tagname; }  
       set  
       {  
         _tagname = value;  
         _writer.Write(String.Format("<{0}>", _tagname));  
       }  
     }  
     public MvcContainer(TextWriter writer)  
     {  
       if (writer == null)  
       {  
         throw new ArgumentNullException("writer");  
       }  
       this._writer = writer;  
     }  
     public MvcContainer(TextWriter writer, String tagname) : this(writer)  
     {  
       this.tagname = tagname;  
     }  
     protected virtual void Dispose(bool disposing)  
     {  
       if (!this.disposed)  
       {  
         this.disposed = true;  
         _writer.Write(String.Format("</{0}>", _tagname));  
       }  
     }  
     [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase")]  
     public void Dispose()  
     {  
       Dispose(true /* disposing */);  
       GC.SuppressFinalize(this);  
     }  
     public void EndContainer()  
     {  
       Dispose(true);  
     }  
   }  

Sólo tenemos que fijarnos en el método Dispose requerido por la interfaz IDisposable en el que escribimos en la salida el elemento de cierre

Utilizamos el writer de la respuesta del contexto, ya que si escribimos con el método Response.Write, el marcado a generar nos aparecerá al principio del resultado, y no en la posición del template Razor utilizado.

Si llamamos al método de extensión creado anteriormente:

 @using (Html.BeginContainer("p"))  
 {  
   <text>Contenido del párrafo</text>  
 }  
Obtendremos la salida deseada:
 <p>Contenido del párrafo</p>