Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
miércoles, 8 de junio de 2011
¡Aprende todo sobre acceso a datos con .NET 4!Como probablemente sabréis, hace unos meses presentamos en CampusMVP el nuevo curso de acceso a datos con .NET 4.0. Un temario amplio y potente, cuyo objetivo es, además de ayudaros a preparar el examen de certificación de especialista en tecnología “70-516 TS: Accessing Data with Microsoft® .NET Framework 4”, aportar una visión detallada de todas las tecnologías de acceso a datos disponibles en la cuarta versión de la plataforma .NET.

Pues bien, CampusMVP ha lanzado recientemente la promoción “verano feliz” que seguro termina de animaros a aquellos que aún necesitáis un empujoncito:
  • acceso durante tres meses al curso de preparación de la certificación, con un descuento brutal sobre el precio habitual,
  • libros en papel y PDF sobre C#, LINQ, Entity Framework y OData indispensables para cualquier desarrollador, escritos por auténticos figuras de este mundillo,
  • acceso a MeasureUp, el simulador oficial del examen de certificación de Microsoft,
  • tres meses de acceso a la biblioteca completa de vídeos de PluralSight sobre tecnologías Microsoft,
  • una mochila EastPak,
  • una camiseta “Feed your brain” muy molona,
Garantía de aprobado CampusMVP… y como siempre, con un tutor (el que os habla) que os ayudará a resolver vuestras dudas a lo largo del curso, la garantía de aprobado de CampusMVP para aquellos que vayáis a presentaros al examen, y por si es poco, la posibilidad de hacerlo a coste cero si tu empresa lo bonifica a través de la Fundación Tripartita.

En mi opinión, se trata de una oportunidad realmente interesante que podéis aprovechar durante este mes y de forma limitada, así que no os lo penséis demasiado ;-)

Más información en: Verano feliz con CampusMVP.

¡Aprende todo sobre acceso a datos con .NET 4!
martes, 7 de junio de 2011
ASP.NET MVCHace cerca de un año ya vimos por aquí cómo crear cuadros de edición de de extensión limitada, o en otras palabras, tags <input type="text" … /> con el atributo Maxlength establecido, con objeto de evitar la introducción de textos más extensos de lo indicado en las restricciones StringLength del Modelo.

En este post vamos a implementar la misma funcionalidad, pero sobre áreas de texto (tag <textarea>), de forma que podamos también limitar el número de caracteres introducidos en este tipo de controles. Como ya sabemos en este caso es algo más complejo, puesto que tenemos que solventar mediante scripts la ausencia del atributo maxlength que sí teníamos disponible en los cuadros de texto convencionales.

Por cierto, no sé qué razones llevarían a no incluir al textarea el atributo maxlength en las versiones anteriores de (X)HTML, pero parece ser que HTML5 va a enmendar esta situación permitiéndolo como atributo válido. De hecho, a día de hoy ya hay algunos navegadores que lo soportan, como las últimas versiones de Chrome y Firefox; IE9 y Opera, por ejemplo, todavía no lo han incorporado… en fin, un poco lo de siempre :-(

1. Situación de partida

En la actualidad, podemos decorar una propiedad del Modelo con el atributo StringLength para limitar el número máximo de caracteres que acepta, y utilizar el atributo DataType para indicar que su edición por defecto debe realizarse mediante un control multilínea, es decir, un área de texto. Hasta ahí, todo es correcto y muy cómodo de implementar.

Error al sobrepasar el máximo de caracteres
Sin embargo, debido a la ausencia del atributo maxlength en los <textarea>, el usuario puede introducir en tiempo de edición todo el texto que desee. Más adelante, al salir del campo o al aceptar el formulario, el mecanismo de validación mostrará el error de que se han sobrepasado el número de caracteres admitidos y no lo dejará continuar, lo cual, como podréis entender, es una auténtica aberración.

Desde el punto de vista del usuario, el número de caracteres no es un dato conocido para él mientras teclea, la limitación no es indicada en ningún punto, y además no obtiene feedback alguno en el momento que ésta ha sido superada, por lo que lo único que puede hacer cuando aparece el error es comenzar a eliminar texto e ir probando periódicamente hasta que alcanza el tamaño deseado. Usabilidad cero, o menos :-(

2. ¿Qué pretendemos conseguir?

Textarea con limitación de caracteresPues básicamente añadir a nuestras aplicaciones ASP.NET MVC la posibilidad de mostrar áreas de edición (<textarea> en HTML) con una capacidad limitada de texto, que será la especificada en las propiedades del Modelo mediante el uso del atributo StringLength.

Como una imagen vale más que todo lo que yo os pueda describir en varios párrafos, a la derecha tenéis una captura de pantalla del resultado que vamos a conseguir.

Crearemos un “control” reutilizable que nos permitirá, de forma muy sencilla y casi totalmente automática, generar editores en áreas de texto limitadas que informarán al usuario en todo momento del número de caracteres que puede introducir, y que no permitirá sobrepasar la longitud especificada en el atributo StringLength.

3. Limitando los <textarea> mediante scripts

Está claro que las funcionalidades que vamos a añadir a las áreas de texto necesitan ser implementadas mediante scripting: es necesario contar los caracteres introducidos por el usuario, eliminar lo que exceda del tope especificado, mostrar información… Afortunadamente, podemos encontrar en la red muchos scripts que realizan estas tareas y que podemos utilizar de forma directa.

El plugin Limit para jQuery es un buen ejemplo: simple, sin grandes ambiciones, pero funciona bien y es muy ligero, así que le delegaremos el trabajo sucio. El siguiente código muestra cómo podemos limitar la longitud para un textarea concreto, y mostrar el contador de caracteres que faltan para completarlo:

<textarea id="myTextarea"></textarea>
<div>Quedan <span id="charsLeft"></span> caracteres</div>
 
<script type="text/javascript" src="/scripts/jquery.limit.js"></script>
<script type="text/javascript">
    $('#myTextarea').limit('140', '#charsLeft');
</script>

Si conseguimos generalizar este código e inyectar el tamaño indicado con el atributo StringLength en la propiedad a editar, tendremos el trabajo hecho :-)

El primer paso que vamos a dar, de momento, es descargar el plugin y copiarlo a la carpeta /Scripts del proyecto, bajo el nombre jquery.limit.js.

4. El enfoque

Vamos a implementar este componente basándonos en las plantillas de edición de ASP.NET MVC, un mecanismo del framework que nos facilita la creación de potentes interfaces de introducción de datos, muy reutilizables y fáciles de construir.

Cuando desde una vista utilizamos el helper Html.EditorFor() para generar el editor de una propiedad concreta, el framework acude a la carpeta /Views/Shared/EditorTemplates en busca de una plantilla (=vista parcial), que genere el interfaz de edición de dicha propiedad.

Normalmente la plantilla que se intenta localizar depende del tipo de dato de la propiedad a editar. Así, por ejemplo, si la propiedad es DateTime se intenta localizar en la citada carpeta una vista parcial llamada DateTime.cshtml (o .aspx si trabajamos con el motor Webforms); si es un int se buscará la plantilla int.cshtml, y así sucesivamente. Puedes ver un ejemplo en otro post, donde explicaba paso a paso cómo implementar un editor de fechas bastante apañado.

Aquí, sin embargo, vamos a utilizar otro enfoque. Existen distintas fórmulas para indicar al framework la plantilla de edición exacta que queremos utilizar para editar una propiedad:
  • en la propia llamada a Html.EditorFor(), que dispone de una sobrecarga en la que podemos indicar el nombre de la plantilla a utilizar para la edición del dato,

  • o bien, sobre la propiedad del modelo usando el atributo [UIHint], suministrándole como parámetro el nombre de la plantilla.
Esta última opción es la más apropiada, puesto que lo indicamos en un único punto para toda la aplicación; es decir, nos permite asociar la plantilla de edición a la propiedad de la siguiente forma:

        [DisplayName("Descripción ampliada")]
        [StringLength(140), Required]
        [UIHint("LimitedTextArea")]
        public string Descripcion { get; set; }

Y de esta forma, al generar el editor para la propiedad Descripcion desde cualquier vista con Html.EditorFor(), renderizaremos el contenido de la plantilla LimitedTextArea.cshtml disponible en /Views/Shared/EditorTemplates. Más sencillo imposible.

5. LimitedTextArea.cshtml

El código de esta plantilla de edición no es demasiado extenso, creo que se puede entender de un vistazo:

@model string
@{  

   // Obtenemos el valor de la anotación MaxLength del modelo...    
   int maxLength = 0;    
   var stringLengthAdapter = ViewContext.ViewData.ModelMetadata
        .GetValidators(ViewContext.Controller.ControllerContext)
        .OfType<StringLengthAttributeAdapter>()
        .FirstOrDefault();
   if (stringLengthAdapter != null)
   {
      var parms = stringLengthAdapter
                    .GetClientValidationRules()
                    .First()
                    .ValidationParameters;
      if (parms.ContainsKey("max"))        
      {
         maxLength = (int)parms["max"];        
      }
   }
   // Obtenemos ahora el id y name del <textarea> a editar...
   var id = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("");
   var name = ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName("");


}
@
Html.TextAreaFor(model => model, new { cols=50, rows=5 })
@if (maxLength > 0)
{
   <div class="textarea-chars" id="msg-textarea-@(id)">        
      Máximo @maxLength caracteres    
   </div>    
   <script type="text/javascript">      $(function () {
         var limit = $('#@id').limit;
         if (limit) {                
            $("#msg-textarea-@id")
               .html("Quedan <span id='left@(id)'></span> caracteres");
            $('#@id').limit('@maxLength', '#left@(id)');
         }        
      });
   </script>
}


En la primera parte, el bloque de código de servidor, se extrae el número máximo de caracteres permitidos desde los metadatos de la propiedad que estamos editando, obteniendo para ello la información indicada en el atributo StringLength. A continuación simplemente se obtienen también los id y name que serán asignados al <textarea> para poder referenciarlo más adelante.

Seguidamente, mediante una llamada al helper Html.TextAreaFor() se genera el área de texto de la forma habitual.

Por último, siempre que maxlength > 0, es decir, que se haya indicado un límite de caracteres concreto, se inserta un <div> para mostrar los mensajes, y se inicializa el plugin jQuery Limit, que se encargará del resto.

En vista del código, seguro que entendéis las tres posibilidades que se pueden dar a la hora de renderizarse este editor:
  • Si la propiedad decorada con el atributo UIHint("LimitedTextArea") no tiene establecido un StringLength, aparecerá el área de edición como siempre, no se realiza ninguna operación adicional.
  • En caso contrario, es decir, si se ha especificado un tamaño máximo, se comprueba la disponibilidad del plugin Limit:
    • si no ha podido detectarse, por ejemplo porque hemos olvidado incluir la biblioteca jQuery.limit.js en la página, simplemente aparecerá un mensaje indicando al usuario el número máximo de caracteres, aunque no se activará ninguna de las funcionalidades ni automatismos previstos,
    • si el plugin ha sido cargado, se activa en el <textarea> que hemos creado.
Por último, observad que el script en todo momento hace referencia al área de texto actual, lo que significa que el componente puede ser utilizado varias veces dentro de la misma página sin que las distintas instancias interfieran entre sí.

¡Y esto es todo! Simplemente creando este archivo LimitedTextArea.cshtml en la carpeta /Views/Shared/EditorTemplates, decorando con el atributo UIHint("LimitedTextArea") las propiedades que queremos editar en el editor multilínea controlado, e incluyendo en la vista la biblioteca jQuery.Limit.js, lo tendremos funcionando.

… aunque esto último todavía podríamos mejorarlo un poco… ;-)

6. Punto extra: ¡Incluye el script por mí, por favor!

Sobre la marcha se me ocurre una mejora que creo que podría resultar interesante: ya que detectamos cuándo el plugin jQuery Limit no está disponible, podríamos insertar un código de tratamiento para este caso e intentar incluirlo de forma automática, asumiendo que se encuentra en la carpeta /Scripts.

El tema consistiría únicamente en sustituir el bloque <script> del código anterior por el siguiente:

    <script type="text/javascript">
        $(function () {
            var limit = $('#@id').limit;
            if (!limit) {
                var script = document.createElement('script');
                script.type = 'text/javascript';
                script.src = "@Url.Content("~/scripts/jquery.limit.js")";
                $("head").append(script);
                limit = $('#@id').limit;
            }
            if (limit) {
                $("#msg-textarea-@id")
                    .html("Quedan <span id='left@(id)'></span> caracteres");
                $('#@id').limit('@maxLength', '#left@(id)');
            }
        });
    </script>

Fijaos que el único cambio es la inserción del primer if, en el que se detecta la inexistencia del plugin, y se añade un tag <script> al DOM para cargarlo. De esta forma, no tendremos que acordarnos de hacer la inclusión de la biblioteca en cada vista donde lo utilicemos :-)

Podéis descargar el código completo del editor y una demo para ASP.NET MVC 3 desde Skydrive, y veréis lo bien que queda y lo fácil que es de utilizar.


Publicado en: Variable not found.
lunes, 6 de junio de 2011

Estos son los enlaces publicados en Variable not found en Facebook y Twitter desde el domingo, 29 de mayo de 2011 hasta el domingo, 05 de junio de 2011. Espero que te resulten interesantes. :-)

Y no olvides que puedes seguir esta información en vivo y en directo desde Variable not found en Facebook, o a través de Twitter.

Publicado en: Variable not found

jueves, 2 de junio de 2011
ASP.NET MVCEs bastante frecuente necesitar obtener el identificador o nombre de un control de entrada que ha sido generado por un helper de ASP.NET MVC. Por ejemplo, si queremos acceder desde scripts al contenido de un cuadro de texto creado de esta forma, obligatoriamente debemos conocer con exactitud qué identificador le asignó el sistema.

Observad el siguiente código de vista, bastante trivial:
@Html.EditorFor(model=>model.Nombre)
Intuitivamente podemos estar seguros de que el control será generado con “Nombre” como nombre de campo e identificador. El código creado será algo así, poco más o menos:
<input type="text" name="Nombre" id="Nombre" />
De esta forma, podríamos acceder al contenido del cuadro de texto por ejemplo con el script mostrado a continuación, que se encarga de convertir su contenido en mayúsculas cuando pierde el foco:
<script type="text/javascript">
    $("#Nombre").blur(function () {
        var mays = $(this).val().toUpperCase();
        $(this).val(mays);
    });
</script>
Y funcionar, funcionaría… algunas veces ;-)

Hay muchos escenarios en los que no está tan claro el identificador que será asignado, sobre todo si estamos generando controles de edición para propiedades de tipos complejos, en vistas parciales, o cuando estamos creando editores personalizados para utilizar con Html.EditorFor(). Este último caso además es especialmente complicado, puesto que desconocemos por completo el nombre de la propiedad para la cual será utilizado.

Lo que suelo hacer en estos casos es utilizar estos dos pequeños helpers, Html.IdFor() y Html.NameFor(), que permiten obtener el nombre e identificador asignado a una propiedad del Modelo accediendo a sus metadatos:

public static class HtmlHelpers
{
    public static string IdFor<TModel, TProperty>(this HtmlHelper<TModel> html, 
        Expression<Func<TModel, TProperty>> expression)
    {
        string propiedad = ExpressionHelper.GetExpressionText(expression);
        return html.ViewData.TemplateInfo.GetFullHtmlFieldId(propiedad);
    }
 
    public static string NameFor<TModel, TProperty>(this HtmlHelper<TModel> html,
        Expression<Func<TModel, TProperty>> expression)
    {
        string propiedad = ExpressionHelper.GetExpressionText(expression);
        return html.ViewData.TemplateInfo.GetFullHtmlFieldName(propiedad);
    }
}

De esta forma, podemos reescribir el script anterior asegurando la corrección del identificador utilizado indicando la propiedad mediante una expresión lambda con tipado fuerte, como solemos hacer en los helpers de edición:
<script type="text/javascript">
    $("#@Html.IdFor(model=>model.Nombre)").blur(function () {
        var mays = $(this).val().toUpperCase();
        $(this).val(mays);
    });
</script>
Y aunque es menos frecuente, también podemos encontrarnos con la necesidad de obtener el nombre de campo del formulario (atributo “name”) a utilizar para un control de edición. Por ejemplo, si queremos construir de forma manual las etiquetas, podemos establecer el nombre utilizando Html.NameFor():
<input type="text" 
       id="@Html.IdFor(model=>model.Nombre)" 
       name="@Html.NameFor(model=>model.Nombre)" />
Espero que os sea de utilidad.

Publicado en: Variable not found.
martes, 31 de mayo de 2011
Desde siempre, C# ha sido el lenguaje por excelencia del framework ASP.NET MVC, y por esta razón es bastante difícil encontrar en la web ejemplos escritos en otros lenguajes, como el popular Visual Basic .NET.

En el caso concreto de la capa Vista, prácticamente nadie escribe ejemplos utilizando Razor y VB.NET, por lo que los desarrolladores que siguen optando por este lenguage para trabajar sobre ASP.NET MVC (e incluso WebPages) lo tienen más complicado para entender y utilizar código existente. Además, a diferencia de lo que podría pensarse, la codificación no es exactamente igual en ambos lenguajes, y a veces no es fácilmente inferible, lo cual añade además un poco de dificultad al usar VB.

En este post vamos a mostrar una tabla de equivalencias entre C# y VB.NET a la hora de codificar distintas construcciones que utilizamos frecuentemente al crear vistas con Razor.

Archivos de vistas o páginas Razor

C#VB.NET
nombrearchivo.cshtmlnombrearchivo.vbhtml

Definición del tipo de datos del Modelo

C#VB.NET
@model Persona@ModelType Persona

Importación de espacios de nombres

C#VB.NET
@using MyApp.Models@Imports MyApp.Models

Definición de clase base de la vista

C#VB.NET
@inherits ClaseBase@Inherits ClaseBase

Bloque de código

C#VB.NET
@{
   // Código C#
}
@Code
   ' Código VB.NET
End Code

Instrucciones de bloque (*)

C#VB.NET
@if(a > b) {
   // Hacer algo
}
@If a > b Then
   ' Hacer algo
End If
(*) aplicable para instrucciones como if, while, for, foreach, using, switch, etc.

Salida de expresión

C#VB.NET
Hola, @Model.NombreHola, @Model.Nombre

Mezcla de código y marcado

C#VB.NET
@if(a > b) { 
   <p>A es mayor que B</p>
}
@If a > b Then
   @<p>A es mayor que B</p>
End If
@if(a > b) {
   @: una línea de texto o HTML
}
@If a > b Then
   @: una línea de texto o HTML
End If
@if(a > b) {
   <text>
      Aquí va texto o HTML
   </text>
}
@If a > b Then
   @<text>
      Aquí va texto o HTML
   </text>
End If

Definición de secciones de un Layout

C#VB.NET
@section Encabezado {
   <h3>Este es el encabezado</h3>
} 
@Section Encabezado
   <h3>Este es el encabezado</h3>
End Section

Creación de helpers

C#VB.NET
@helper Tabla(int num) {
   <ul>
   @for (int i = 1; i < 11; i++)
   {
      <li>@num x @i = @(num*i) </li>
   }
   </ul>
}
@Helper Tabla(num As Integer)
   @:<ul>
   For i = 1 To 10
      @<li>@num x @i = @(num * i) </li>
   Next
   @:</ul>
End Helper

Bloques de funciones

C#VB.NET
@functions {
    int suma(int a, int b)
    {
        return a + b;
    }
}
@Functions
    Function suma(a As Integer,
                  b As Integer) As Integer
        Return a + b
    End Function
End Functions

Razor templated delegates

C#VB.NET
@{
    Func<dynamic, object> bold =
        @<strong>@item</strong>;
}

Uso: @bold("Esto en negrilla")
@Code
   Dim bold =
      Function(item As Object)
         @<strong>@item</strong>
      End Function
End code

Uso: @bold("Esto en negrilla")
@helper Lista(
   Func<dynamic, HelperResult> templ,
   params dynamic[] args)
{
    foreach(dynamic item in args)
    {
        @templ(item)
    }
}

Uso:
<ul>
   @Lista(@<li>@item</li>,
          1, 2, 3.5, "hola",
          DateTime.Now)
</ul>
@Helper Lista(templ
    As Func(Of Object, HelperResult),
    ParamArray args() As Object)

   For Each item In args
      @templ(item)
   Next
End helper

Uso:
<ul>
   @Lista(Function(item As Object)
            @<li>@item</li>
          End Function,
          1, 2, 3.5, "hola",
          DateTime.Now)
</ul>

Espero que no se me haya quedado por detrás ninguna de las construcciones habituales. De todas formas, si detectáis alguna ausencia, no dudéis en avisarme y lo incluyo en este mismo post.

Publicado en: Variable not found.
lunes, 30 de mayo de 2011
Estos son los enlaces publicados en Variable not found en Facebook y Twitter desde el lunes, 23 de mayo de 2011 hasta el domingo, 29 de mayo de 2011. Espero que te resulten interesantes. :-)
Y no olvides que puedes seguir esta información en vivo y en directo desde Variable not found en Facebook, o a través de Twitter.

Publicado en: Variable not found