Creando un DropDownlist en Cascada Genérico para ASP usando JQuery



Inspirado en los tutoriales del gran educador Juan Carlos Zuluaga, me motive a escribir un articulo sobre como crear (o usar) un Dropdownlist en cascada, es decir, que los datos que haya en un lado B (como ciudad) dependa del dato seleccionado en el lado A (como pais).


Si bien es cierto que realizar un Ddl en cascada es algo simple y sencillo, no deja de ser cierto que la duplicidad de código es algo que últimamente preocupa mas de la cuenta a las empresas, quienes escogen a un programador por sus conocimientos técnicos, pero de igual forma prefieren que esos conocimientos técnicos sean óptimos.

Por eso he decidido explicar el modo en el que yo lo he hecho, claro que pueden haber otros modos, pero, este es el mas simple que he visto hasta el momento, ya que cuando lo hice, lo hice realmente con conocimientos muy básicos de c#, así que esto es lo que garantiza que esta hecho por un novato para novatos :D



Iniciemos


Lo primero que vamos a hacer es crear 3 modelos,

Pais=>Ciudad=>Client  


public class Country
{
    public int Id { getset; } 
    public string Name { getset; } 
    public string Denomym { getset; } 
    public ICollection<City> Cities { getset; }
}
public class City
{
    public int Id { getset; } 
    public string Name { getset; } 
    public string Denomym { getset; } 
    public Country Country { getset; } 
    public ICollection<Client> Clients { getset; }
}
public class Client
{
    public int Id { getset; } 
    public string Name { getset; } 
    public string Lastname { getset; } 
    public City City { getset; }
 
}
La idea es que al seleccionar un país debe filtrar y traerme las ciudades del mismo.

Posterior a eso, deberemos de crear un ViewModel que aloje los datos del modelo Client, y al cual le complementaremos los datos que no posee el mismo, en mi caso particular, yo siempre prefiero ponerlo a heredar de la clase base, y completar las que faltan, pero en este ejemplo lo hare lo mas simple posible.


public class ClientViewModel //: Client
{
    public int Id { getset; }
    public string Name { getset; }
    public string Lastname { getset; }
    public City City { getset; }
 
    [Display(Name = "City")]
    [Range(1int.MaxValue, ErrorMessage = "You must select a City.")]
    public int CityId { getset; }
 
    [Display(Name = "Country")]
    [Range(1int.MaxValue, ErrorMessage = "You must select a Country.")]
    public int CountryId { getset; }
    //This is added because I dont want to save country on client, but I need on the ViewModel
    public ICollection<Country> Country { getset; }
    //This is added to receive the Listed items
    public IEnumerable<SelectListItem> Cities { getset; }
    public IEnumerable<SelectListItem> Countries { getset; }
 
}

Creamos un Helper para convertir cualquier entidad a un SelectListItem, esto no es necesario, pero es una forma de evitar la duplicidad de código y mas cuando esto es algo que normalmente no varia.


public class GenericSelectList
{
    public IEnumerable<SelectListItem> CreateSelectList<T>(IList<T> entities, Func<Tobject> funcToGetValue, Func<Tobject> funcToGetText)
    {
        return entities
            .Select(x => new SelectListItem
            {
                Value = funcToGetValue(x).ToString(),
                Text = funcToGetText(x).ToString()
            });
    }
}

Ahora es el momento de Crear nuestro método que se va a encargar de traer nuestros datos, yo personalmente lo pongo en el controlador que se va a encargar de manipular nuestra información, pero, perfectamente puede ser puesto en un Helper.


public JsonResult GetCitiesFromCountry(int id)
 {
     var dbList = _context.Cities.Where(m => m.Country.Id == id)
         .Select(c => new
         {
             Id = c.Id,
             Name = c.Name
         });
 
     return Json(dbList);
 }

Después creamos el control que se encargara de traer nuestros datos para mandarlo a la vista y tras eso obtener los datos, convertirlo para mandarlo a la base de datos.

Por obvias razones acá no voy a usar patrones ni nada por el estilo, pues el único interés es explicar el archivo Js genérico para llenar un ddl en cascada.


public class ClientsController : Controller
  {
      private readonly AppContext _context;
      private readonly GenericSelectList _genericSelectList;
      
      public ClientsController(AppContext context)
      {
          _context = context;
          _genericSelectList = new GenericSelectList();
      }
     
       public async Task<IActionResult> Create()
      {
          var countries = new List<Country>
          {
              new Country
              {
                  Id = 0,
                  Name = "Choose one Country"
              }
          };
       
          countries.AddRange(await _context.Countries.ToListAsync());       
          var model = new ClientViewModel
          {
              Countries = _genericSelectList.CreateSelectList(countries, x => x.Id, x => x.Name),
          }; 
          return View(model);
      }
 
      [HttpPost]
      [ValidateAntiForgeryToken]
      public async Task<IActionResult> Create(ClientViewModel model)
      {
          if (ModelState.IsValid)
          {
              var city = await _context.Cities.FindAsync(model.CityId); 
              var client = new Client
              {
                  Name = model.Name,
                  Lastname = model.Lastname,
                  City = city
              }; 
              _context.Add(client);
              await _context.SaveChangesAsync();
              return RedirectToAction(nameof(Index));
          }
          return View(model);
      }
  }

Creamos la vista y referenciamos el script en nuestra pagina, y creamos una variable mediante javascript que se llame "gUrlDdl" a la cual le debemos asignar la url donde esta el método que trae los datos., no olvides asignarle al select Maestro la clase "MasterDdl" y al detalle "DetailDdl"


@model GenDdlSampleCoreMvc.Models.ClientViewModel
 
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
 
            <div class="form-group">
                <label asp-for="Country" class="control-label"></label>
                <select asp-for="CountryId" 
                        asp-items="Model.Countries"
                        class="form-control MasterDdl">
                </select>
                <span asp-validation-for="Country" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="City" class="control-label"></label>
                <select asp-for="CityId" 
                        asp-items="Model.Cities" 
                        class="form-control DetailDdl">                
                </select>
                <span asp-validation-for="City" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Lastname" class="control-label"></label>
                <input asp-for="Lastname" class="form-control" />
                <span asp-validation-for="Lastname" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>
<script src="~/lib/sGenCascadeDdl/dist/sgencascadeddl.js"></script>
 
<script>
         var gUrlDdl = '@Url.Action("GetCitiesFromCountry""Clients")';
</script>






Y listo, ya tenemos todo lo que necesitamos para nuestro Dropdown List en Cascada


Puedes ver el código de ejemplo, así como el script necesario en mi repositorio.

https://github.com/sgermosen/GenericCascadeDropDownList

No hay comentarios:

Publicar un comentario

Pages