[Truco] ASP.NET MVC & Enums

Hace mucho que no escribía nada en el blog y tengo que empezar a coger otra vez la rutina y nada mejor para eso que comenzar con un truquito para utilizar en nuestros desarrollos en ASP.NET MVC.

Como buen truco no me voy a enrollar demasiado en las explicaciones ni teorías  y me voy a centrar más en el código para que se pueda utilizar de una manera simple.

El Problema

Imaginemos que tenemos un modelo de datos que utiliza una enumeración.

public class Persona
    {
        public int Id { get; set; }
        public string Nombre { get; set; }
        public int Edad { get; set; }
        public Genero Genero { get; set; }
    }
    public enum Genero
    {
        Hombre,
        Mujer,
        Geek
    }

Una vez tenemos nuestra entidad Genero el controlador y las vistas de forma automática con Visual Studio para no entretenerme mucho con el ejemplo. De esta manera ya lo tengo todo preparado con un solo click.

Nuestra vista de inserción tendría esta pinta:

VistaInicial

Como se puede observar la vista se ha creado perfectamente para añadir nuevas personas, pero no se a mapeado correctamente la enumeración para seleccionar el sexo de la persona.

Si lo intentáramos bindar directamente con el modelo tendríamos igualmente el problema porque no sabe mapear la enumeración directamente.


NOModel

Solución Rápida

A los elementos html de MVC se le pueden pasar directamente un lista para mostrar los datos y gracias a eso podemos forzar el mapeo de una forma muy fácil.

<div class="editor-field">
       @Html.DropDownListFor(model => model.Genero, 
              new SelectList(Enum.GetValues(typeof(Genero))))</div>

En este truco lo único que hacemos es pasar al control la lista de elementos que acepta haciendo un cast de la enumeración y ya tenemos la vista funcionando perfectamente.

VistaFinal

Solución Elegante

Si en nuestra aplicación utilizamos muchas entidades con enumeraciones nos interesaría reutilizar el código y no tener que hacer la conversión cada vez. Para eso podemos extender el helper del DropDownListFor para tener nuestro propio helper que acepte enumeraciones.

 public static class HtmlExtensions
    {
        public static MvcHtmlString DropDownListForEnum<TModel, TEnum>(
            this HtmlHelper<TModel> htmlHelper,
            Expression<Func<TModel, TEnum>> expression)
        {

            var list = new SelectList(Enum.GetValues(typeof (TEnum))).ToList();
            var selectList = list.ToList();
            return htmlHelper.DropDownListFor(expression, selectList);
        }
    }

Con nuestro nuevo helper ahora podemos utilizar las enumeraciones del modelo de una forma más natural y práctica.

<div class="editor-field">
           @Html.DropDownListForEnum(model => model.Genero )</div>

Y la aplicación funciona perfectamente con nuestras enumeraciones.

Lista

El toque final

Ya tenemos nuestra extensión del control, pero que pasa si queremos añadirle atributos html como el control original, ahora mismo tal como está no lo permite. Pues lo único que tenemos que hacer es sobrecargar el nuevo control para que acepte este nuevo parámetro y ya lo tendremos solucionado.

 public static class HtmlExtensions
{
     public static MvcHtmlString DropDownListForEnum<TModel, TEnum>(
         this HtmlHelper<TModel> htmlHelper,
         Expression<Func<TModel, TEnum>> expression,
         Object htmlAttributes)
     {
         var list = new SelectList(Enum.GetValues(typeof (TEnum))).ToList();
         var selectList = list.ToList();
         return htmlHelper.DropDownListFor(expression, selectList, htmlAttributes);
     }
     public static MvcHtmlString DropDownListForEnum<TModel, TEnum>(
         this HtmlHelper<TModel> htmlHelper,
         Expression<Func<TModel, TEnum>> expression)
     {
         return htmlHelper.DropDownListForEnum(expression, null );
     }
}

Ahora podemos especificar atributos Html para el renderizado del control.

<div class="editor-field">
         @Html.DropDownListForEnum(model => model.Genero, 
             new {@class="dropdown" } )</div>

Y la personalización del control se mostraría con el estilo especificado en el helper.

ListaExtendida

Espero que este truco os sea útil para vuestros desarrollos y si queréis profundizar más con las enumeraciones en MVC podéis ver la entrada de Eduard Tomas en su blog.

Nuevo artículo en el blog logro desbloqueado ;-)

6 comentarios en “[Truco] ASP.NET MVC & Enums

  1. Interesante, y más que la aplicación del enum en un dropdown me llevo algo aprendido que seguro que en el futuro me será más importante, que es la extensión de un método de HtmlExtensions que la verdad es que llevaba tiempo intentando hacerlo con resultados no muy efectivos. Muchas gracias.

      • Pues tengo algo… el caso es que no sé como explicarlo muy bien… a ver si me sale, si no te mando un ejemplo en código que lo tengo.
        Tengo un formulario de registro de una aplicación. Este formulario tiene sus correspondientes validaciones que son «unobtrusive».
        Esto en el registro simple funciona perfecto, ahora digamos, que quiero darle una vuelta de tuerca y quiero validar con una llamada ajax que el usuario que introduces no existe (cuanto saltas al siguiente campo, para que no me tengas que enviar el formulario).
        Teniendo todo ese código generado, en el momento que haces el $.get para realizar la validación a través de una Acción se produce el efecto mariposa y saltan todas las del formulario. (Realmente saltan cuando se produce la respuesta y no con la llamada, pero supongo que será irrelevante).
        He evitado que salten todas con e.preventDefault() pero… podría apostar a que no está bien, tengo la sospecha de que debería crear una validación para el campo del lado del servidor, pero no lo tengo claro del todo.

        Saludos

      • Marc Rubiño dijo:

        La verdad es que tendría que verlo para entender donde está el problema. Pero si quieres un día lo miramos ;-)

  2. Juan dijo:

    Hola, muy bien tutorial. Tengo una consulta, como haría manipulación HTML dependiendo de la enumeracion elegida? Ejemplo si tengo «male» que me muestre un color azul, de lo contrario «female» un color rosa. Esto es solo un ejemplo, tengo un sencillo sistema de tickets con estados, el status opened deberia mostrar el input para enviar comentario, en cambio closed deberia ocultarlo. Sería en razor @if(condition) etc etc? O en el controlador?? Pero como exactamente? Gracias

Deja un comentario