17 de abril de 2013

Crear dropdowns en cascada con MVC 3 y jQuery

Una cosa muy común que encontramos en cualquier sitio es la dependencia de datos. Podemos tener una forma donde nos pidan ingresar nuestro país de residencia junto con el estado, y cada vez que el usuario cambie el dropdown del país se debe actualizar la lista de estados. La manera correcta de hacer esto es evitando que se refresque la página completa tratando de conseguir los datos del dropdown dependiente. Les mostraré cómo hacer esto utilizando MVC 3 con jQuery y un ejemplo de fabricantes y modelos de automóviles.



 Para hacer esto yo estoy usando Visual Studio 2010 con .NET Framework 4 y MVC 3. Creamos un proyecto nuevo y lo podemos escoger vacío o como aplicación de Internet. Lo primero es crear dos objetos, uno será el Fabricante y el otro el modelo del auto, ambos tendrán dos propiedades, un Id numérico y un Nombre.

public class Fabricante
{
     public int Id { get; set; }
     public string Nombre { get; set; }
}

public class ModeloAuto
{
     public int Id { get; set; }
     public string Nombre { get; set; }
}

Ustedes pueden utilizar la base de datos que prefieran pero para este ejemplo voy a crear un repositorio con la información que necesitamos al igual que una interfaz ICarroRepositorio.
public class AutoRepositorio : IAutoRepositorio
{  
   public IQueryable<fabricante> GetFabricantes()
   {
 return new List<fabricante>
        {
            new Fabricante { Id = 1, Nombre = "Mazda" },
            new Fabricante { Id = 2, Nombre = "Ford" },
            new Fabricante { Id = 3, Nombre = "Nissan" }
        }.AsQueryable();
   }

   public IQueryable<modeloauto> GetModelos(int id)
   {
 var modelos = new List<modeloauto>();

            if (id == 1) {
                modelos.AddRange(new List<modeloauto> { 
                    new ModeloAuto { Id = 1, Nombre = "Mazda3" },
                    new ModeloAuto { Id = 2, Nombre = "Mazda5" },
                    new ModeloAuto { Id = 3, Nombre = "Mazda CX-5" }
                });
            } else if (id == 2) {
                modelos.AddRange(new List<modeloauto> {
                    new ModeloAuto { Id = 1, Nombre = "Fiesta" },
                    new ModeloAuto { Id = 2, Nombre = "Focus" },
                    new ModeloAuto { Id = 3, Nombre = "Fusion" }
                });
            } else if (id == 3) {
                modelos.AddRange(new List<modeloauto> {
                    new ModeloAuto { Id = 1, Nombre = "March" },
                    new ModeloAuto { Id = 2, Nombre = "Sentra" },
                    new ModeloAuto { Id = 3, Nombre = "Tida" }
                });
            }

        return modelos.AsQueryable();
   }
}

public interface IAutoRepositorio 
{
    IQueryable<fabricante> GetFabricantes();
    IQueryable<modeloauto> GetModelos(int idFabricante);
}

A continuación vamos a crear un ViewModel, que será el modelo procesado que le pasaremos a la vista, en él vamos a crear dos listas vacías, una para los Fabricantes y otra para los Autos.
public class IndexViewModel
{
    public string Fabricante { get; set; }
    public int Modelo { get; set; }

    public SelectList FabricantesDisponibles { get; set; }
    public SelectList ModelosDisponibles { get; set; }

    public IndexViewModel()
    {
        FabricantesDisponibles = new SelectList(
            Enumerable.Empty<fabricante>(), 
     "Id", 
     "Nombre");
        ModelosDisponibles = new SelectList(
     Enumerable.Empty<modeloauto>(), 
            "Id", 
     "Nombre");
        }
}

Y ahora nuestro controlador principal, aquí vamos a llenar las listas vacías con los datos del repositorio.
public class HomeController : Controller
{
    private IAutoRepositorio autoRepositorio = new AutoRepositorio();
        
    public ActionResult Index()
    {
        ViewBag.Message = "DropDowns en Cascada";
        var modelo = new IndexViewModel();

        modelo.FabricantesDisponibles = new SelectList(
     carroRepositorio.GetFabricantes(), 
     "Id", 
     "Nombre");
        modelo.ModelosDisponibles = new SelectList(
     carroRepositorio.GetModelos(1), 
     "Id", 
     "Nombre");

        return View(modelo);
    }
}

Esta es la vista que usaremos de ejemplo con dos dropdowns.
@model DropDownCascada.Models.IndexViewModel
<h2>@ViewBag.Message</h2>
@using (Html.BeginForm()) {
    <div class="editor-label">
        @Html.LabelFor(m =&amp;gt; m.Fabricante)
    </div>
<div class="editor-field">
    @Html.DropDownListFor(
        m =&amp;gt; m.Fabricante, 
 Model.FabricantesDisponibles)
</div>
<div class="editor-label">
    @Html.LabelFor(m =&amp;gt; m.Modelo)
</div>
<div class="editor-field">
    @Html.DropDownListFor(
        m =&amp;gt; m.Modelo, 
        Model.ModelosDisponibles)
</div>
<input type="submit" value="Submit" />

Este es el controlador que cambia los valores de la lista de modelos de autos cuando el usuario selecciona un fabricante distinto, utiliza el atributo HttpPost para indicar que esa acción solo recibirá peticiones de tipo POST. Regresan los datos en forma de JSON.
public class AutoController : Controller
{
    private IAutoRepositorio autoRepositorio = new AutoRepositorio();

    [HttpPost]
    public ActionResult Modelos(int id)
    {
        var modelos = autoRepositorio.GetModelos(id);

        return Json(new SelectList(modelos, "Id", "Nombre"));
    }
}

Y finalmente, el script de jQuery que mandará llamar la acción para cambiar los valores del segundo dropdown. Utilizamos un llamado a Ajax con jQuery y damos los valores a las distintas configuraciones:
  • Url: La dirección a donde se manda la solicitud.
  • Data: Los parámetros que se le enviarán al servidor.
  • DataType: El tipo de dato que estamos esperando recibir del servidor.
  • Type: El tipo de solicitud("POST" o "GET").
  • Error: La función que se va a llamar si la solicitud falla.
  • Success: La función que se va a llamar si la solicitud es aceptada.


    <script type="text/javascript">
        function getModelos(id) { 
            $.ajax({
                url: "@Url.Action("Modelos", "Auto")",
                data: {id: id},
                dataType: "json",
                type: "POST",
                error: function(){
                    alert("Ha ocurrido un error.");
                },
                success: function(data){
                    var items = "";
                    $.each(data, function(i, item){
                        items += "<option value=\"" +
                                 item.Value + "\">" +
                                 item.Text + "</option>";
                    });

                    $("#Modelo").html(items);
                }
            });
        }

        $(document).ready(function(){
            
            
            $("#Fabricante").change(function(){
                var id = $("#Fabricante").val();
                getModelos(id);
            });
        });
    </script>

Con el código de jQuery ponemos un manejador de eventos que se activará cuando seleccionen un fabricante distinto, el cual hace una llamada a AJAX que está esperando los datos en forma de JSON. Si el llamado a AJAX es exitoso, se hace una iteración para llenar la lista del segundo dropdown con las nuevas opciones. Y con este código es posible hacer dropdowns en cascada.

Si desean descargar el ejemplo de este post lo pueden hacer aquí.

No hay comentarios:

Publicar un comentario