Cómo crear un Cascading DropDownList en ASP.NET MVC

Seguramente, en algunos de nuestros desarrollos ASP.NET MVC, nos hemos visto en la necesidad de crear un formulario de tipo POST, que contenga listas desplegables (<select />) relacionadas entre sí, en función del elemento seleccionado en una de ellas (Cascading DropDownList).

En este artículo veremos cómo crear un Cascading DropDownList de dos listas desplegables, cargando de forma dinámica (AJAX) la segunda lista, en función del elemento seleccionado en la primera. 

cascading-ddl

Los Modelos de datos

Lo primero que debemos hacer, es crear los Modelos que proveerán de datos a las dos listas desplegables de nuestro ejemplo.

Como origen de datos, utilizaremos la base de datos de pruebas Northwind de Microsoft (Scripts disponibles para descargar al final del Post). Los Modelos de datos los generaremos a partir de las tablas Territories y Region, relacionadas entre sí por una clave foránea (FK_Territories_Region) de la siguiente manera:

territories-region

Lo que queremos reproducir, es la interacción entre el primer Modelo Region (primera lista desplegable), tal que al seleccionar un elemento, solo se carguen en la segunda lista desplegable (Territories) los elementos que estén relacionados por su clave foránea.

Crearemos entonces los siguientes Modelos de Datos en nuestra aplicación:

    public class Region
    {
        public Region()
        {
            Territories = new HashSet<Territories>();
        }

        public int RegionId { get; set; }

        [Required]
        public string RegionDescription { get; set; }

        // PROPIEDAD DE NAVEGACIÓN
        public ICollection<Territories> Territories { get; set; }
    }
    public class Territories
    {
        [Key]
        public string TerritoryId { get; set; }

        [Required]
        public string TerritoryDescription { get; set; }

        public int RegionId { get; set; }

        // PROPIEDAD DE NAVEGACIÓN
        [ForeignKey("RegionId")]
        public Region Region { get; set; }
    }

 

El Controlador

En la Acción Index() del Controlador (en este caso HomeController), cargaremos los datos de la primera lista desplegable (Regions) en la propiedad ViewBag.

Nota: Para realizar este ejemplo, es necesario disponer de una base de datos con las tablas anteriormente mencionadas, así como tener instalado en nuestro proyecto el ORM Entity Framework 6 para el acceso a los datos. Para saber cómo crear una base de datos de pruebas con Entity Framework 6 en ASP.NET MVC, pueden aprender todo lo necesario en este artículo: Entity Framework 6 y Sql Server Compact CE en ASP.NET MVC 5.

El código para el Controlador HomeController sería el siguiente:

    public class HomeController : Controller
    {
        private AppDbContext _dbContext;
        private List<SelectListItem> _regionsItems;
        
        public ActionResult Index()
        {
            using (_dbContext = new AppDbContext())
            {
                // CARGAMOS EL DropDownList DE REGIONES
                var regions = _dbContext.Regions.ToList();
                _regionsItems = new List<SelectListItem>();
                foreach (var item in regions)
                {
                    _regionsItems.Add(new SelectListItem
                    {
                        Text = item.RegionDescription,
                        Value = item.RegionId.ToString()
                    });
                }
                ViewBag.regionsItems = _regionsItems;                
            }

            return View();
        } 

        // AQUÍ EL RESTO DE LAS ACCIONES...


     }

Para recuperar de la base de datos los datos de la segunda lista desplegable (Territories), crearemos una nueva Acción (GetTerritoriesList(int RegionId)) que devolverá una estructura JSON (JsonResult) en función de la Región seleccionada en la primera lista desplegable (int RegionId).

        public JsonResult GetTerritoriesList(int RegionId)
        {
            using (_dbContext = new AppDbContext())
            {
                var territories = _dbContext.Territories.Where(x => x.RegionId == RegionId).ToList();
                return Json(territories, JsonRequestBehavior.AllowGet);
            }                            
        }

 

La Vista

En la Vista (Index.cshtml) , crearemos las dos listas desplegables (DropDownList):

<div class="row">
    <div class="form-group">
        @Html.DropDownList("ddlRegions", ViewBag.regionsItems as List<SelectListItem>,
                           " -- Seleccionar Regiones --", new { @class = "form-control" })
        <br />
        @Html.DropDownList("ddlTerritories", new List<SelectListItem>(),
                           " -- Seleccionar Territorios --", new { @class = "form-control" })

    </div>
</div>

Como vemos en el código, la primera lista desplegable (ddlRegions) carga todos elementos de la base da datos, y la segunda lista (ddlTerritories), estará vacía hasta que se seleccione un elemento de la primera.

 

Recuperando los datos con jQuery - AJAX

En este punto, quedaría lo más importante: Recuperar los datos de la lista desplegable ddlTerritories, en función del elemento seleccionado en la lista ddlRegionsmediante jQuery AJAX.

Nota: Por supuesto, tenemos que tener instaladas y referenciadas en nuestro proyecto las librerías jQuery, para habilitar entre otras las funcionalidades AJAX.

Para ello y por último, crearemos un script jQuery que llame mediante AJAX a la Acción GetTerritoriesList(int RegionId), y recupere en formato JSON los datos de la lista desplegable ddlTerritories.

El código lo definiríamos en la sección @section scripts {...}, y sería el siguiente:

@section scripts {

    <script>
        $(document).ready(function () {
            $("#ddlRegions").change(function () {
                $.get("/Home/GetTerritoriesList", { RegionId: $("#ddlRegions").val() }, function (data) {
                    // VACIAMOS EL DropDownList
                    $("#ddlTerritories").empty();
                    // AÑADIMOS UN NUEVO label CON EL NOMBRE DEL ELEMENTO SELECCIONADO
                    $("#ddlTerritories").append("<option value> -- Seleccionar Territorios de " + $("#ddlRegions option:selected").text() + " --</option>")
                    // CONSTRUIMOS EL DropDownList A PARTIR DEL RESULTADO Json (data)
                    $.each(data, function (index, row) {
                        $("#ddlTerritories").append("<option value='" + row.TerritoryId + "'>" + row.TerritoryDescription + "</option>")
                    });
                });
            });
        });
    </script>

}

 

Descargas

Script base de datos - northwind.sql

  Compartir


  Nuevo comentario

El campo Comentario es obligatorio.
El campo Nombre es obligatorio.

  Comentarios

Itterrep Itterrep

A mi me pasa lo mismo que a Cristian al querer pasar las selecciones de ambos DDL al controlador, hago agua, ya llevo varios dias... alguien tiene una idea?
Cristian Cristian

He intentado de varias formas, pero me sigue dando los valores del select como "undefined", alguien me podría ayudar?
Santiago Figueroa Santiago Figueroa

Y en caso de ser un Edit, como se devolvería el valor guardado en Región y Territorio sin que Territorio cambie y me pida seleccionar uno nuevo.

Luis Gerardo Luis Gerardo

Para resolver el problema de que no muestra datos o muestra Undefined, en mi caso el return del contrlador es un poco diferente al del ejemplo:
Mi codigo => return Json(new SelectList(VigenciaList, "RowId", "Vigencia"));

Con esto retorno un Json con la estructura del SelectList {disabled, group, selected, text, value} luego en el script tengo lo siguiente:

Mi script => $("#Vigencia").append("<option value='" + row.value + "'>" + row.text + "</option>")

Noten que no hago referencia a los nombres "RowId" y "Vigencia" que retorna el Json, sino que utilizo "value" y "text"

Espero que les pueda ayudar a resolver este problema.
Brayan Aldana Brayan Aldana

Si no les presenta los datos en el @Html.DropDownList("ddlTerritories", new List<SelectListItem>(),.... posiblemente les sirva poner esto db.Configuration.ProxyCreationEnabled = false; en GetTerritoriesList, así:
public JsonResult GetSubjectsList(int AreaID)
{
db.Configuration.ProxyCreationEnabled = false;

List<Subject> subjects = db.Subjects.Where(x => x.Area.AreaID == AreaID).ToList();

return Json(subjects, JsonRequestBehavior.AllowGet);

}
Rolando Rolando

Hago todos los pasos como dice el ejemplo utilizando mis propias bases de datos pero no me devuelve en el row los valores que necesito

$.each(data, function (index, row) {
$("#ddlTerritories").append("<option value='" + row.TerritoryId + "'>" + row.TerritoryDescription + "</option>")

En mi ejemplo tengo los campos de la base de datos como ID y Descripción

$.each(data, function (index, row) {
$("#ddlTerritories").append("<option value='" + row.Id + "'>" + row.Descripcion + "</option>")+

Agradeceria alguna ayuda escribirme a chokisoft@gmail.com


Jorge Jorge

No funciona completo solo muestra el dropdownlist de regiones. Luego no hace nada en mi caso al seleccionar territorios. Me imagino as llegó lo esta bien en el script
Oneal Oneal

Hola, como podria implementar esta misma logica pero para 3 dropdown dependientes?
David Mu&#241;oz David Muñoz

Excelente artículo, muchas gracias.
ana ana

solo me muestra los datos de en este caso la region en la de territorios no me devuelve nada cheque todo y al parecer tengo todo bien pero no se en que me equivoque o si tengo mal algo




Rafael Acosta Administrador Rafael Acosta

@Vicente Maldonado:

Gracias por tu comentario,


En principio la utilización de JsonRequestBehavior.AllowGet es necesaria, ya que la llamada Ajax que se está haciendo desde el Cliente es de tipo Http GET ( $.get("/Home/GetTerritoriesList" ) ).


Lo único que se me ocurre es que estés utilizando ASP.NET Core, donde JsonRequestBehavior.AllowGet ya no es necesario especificarlo en la respuesta Json.


Vicente Maldonado Vicente Maldonado

buen día, He probado el código, funciona el llamado al public JsonResult GetTerritoriesList(int RegionId), aparentemente retorna datos, pero no los presenta en el @Html.DropDownList("ddlTerritories", new List<SelectListItem>(),.... solo muestra indefined.
Tuve que cambiar return Json(territories, JsonRequestBehavior.AllowGet); por return Json(territories)
Gracias por su ayuda.


Utilizamos cookies propias y de terceros para mejorar nuestros servicios y ofrecerle una mejor experiencia de navegación. Si continúa navegando consideramos que acepta su uso. Más información   Acepto