ASP.NET MVC y Ajax: fácil no, facilísimo (ii)

Hola a todos! En este post (continuación del post anterior ASP.NET MVC y Ajax) voy a comentaros algunas cosillas más sobre ASP.NET MVC y ajax utilizando jQuery. En este caso vamos a ver como implementar un clásico de Ajax: las combos encadenadas.

Vamos a hacer el manido ejemplo de continentes y paises: seleccionas un continente en la combo y via Ajax se rellena la combo de paises. Vereis que sencillo es crear combos encadenadas con ASP.NET MVC y jQuery.

Lo primero es crear una vista que tenga las dos combos que queramos (yo como siempre modifico la vista inicial (Views/Home/Index.aspx).

El código que he puesto es:

<h1>Ajax y MVC... tan fácil como parece!</h1>
Escoja continente y deje que el poder de Ajax le muestre algunos paises...

<select id="cmbContinente">
    <option value="---" selected="selected">Escoja continente...</option>
    <option value="eur">Europa</option>
    <option value="ame">America</option>
    <option value="asi">Asia</option>
    <option value="afr">Africa</option>
</select>
<select id="cmbPaises"></select>

Excelente… ya tenemos las dos combos. Ahora empieza lo interesante: cuando se seleccione un elemento de la combo de continentes vamos  a realizar una petición ajax al controlador, para que nos devuelva que países se corresponden al continente seleccionado…

Como siempre jQuery viene en nuestra ayuda:

<script type="text/javascript">
    $(document).ready(function() {
        $("#cmbContinente").change(function() {
            fillCombo("cmbPaises", $("#cmbContinente").val());
        });
    });
</script>

Si visteis mi post anterior este código ya no os sonará tan extraño. Si no lo habeis visto, o bien no os acordáis ahí va un rápido resumen: con $(document) obtengo el manejador jQuery al documento actual, y llamo a su método ready. El método ready se ejecuta cuando el documento está “listo” (esto es todo el árbol DOM inicializado), y como parámetro espera una función. En mi caso le paso una función anónima con el código a ejecutar.

Lo que hace la función anónima es muy sencillo. Recordad que en jQuery la expresión $(“#xx”) obtiene un manejador jQuery al elemento DOM cuyo id sea “xx”. Así pues obtengo el manejador jQuery de la combo cuyo id es “cmbContinente” y me suscribo a su evento change. Cuando se usa jQuery generalmente no nos suscribimos a los eventos de los elementos DOM usando la sintaxis clásica onXXX, sinó que usamos las propias funciones de jQuery. En mi caso uso la función change() que se correspondería el evento onChange. La razón de actuar así es que jQuery intenta ofrecer un modelo de eventos unificado entre distintos navegadores. Si veis el código, simplemente se le pasa al método change una función anónima con el código a ejecutar cuando se lance el evento change: una llamada a una función fillCombo  que definiremos luego. A esta fillCombo le pasamos el id de la combo que vamos a actualizar y el valor seleccionado de la combo de continentes. Fijaos que de nuevo usamos jQuery para saber el valor seleccionado de la combo: obtenemos un manejador jQuery a la combo y llamamos al método val().

Bien, vamos ahora a ver el código de la función fillCombo. Esta función coje dos parámetros updateId y value (el id de la combo a rellenar y el valor seleccionado de la primera combo):

<script type="text/javascript">
    function fillCombo(updateId, value) {
        $.getJSON("<%= Url.Action("PaisesPorContinente") %>" 
+ "/" + value, function(data) { $("#"+updateId).empty(); $.each(data, function(i, item) { $("#"+updateId).append("<option id='"
+ item.IdPais +"'>" + item.Nombre
+ "</option>"); }); }); } </script>

Veamos que hace exactamente fillCombo:

  • Usa la función getJSON de jQuery. Esta función realiza una llamada a la URL indicada (en mi caso una llamada a la acción PaisesPorContinente), usando AJAX y espera que dicha función devuelva un resultado en formato JSON. Además parsea el resultado JSON y lo convierte en un objeto javascript. Para los que no sepáis que es JSON os diré que es un lenguaje para representar objetos (tal y como puede ser XML) pero mucho más compacto, y además más “compatible” con javascript. Para más información pasaros por json.org.
  • La función getJSON admite dos parámetros: el primero es la URL y el segundo una función a ejecutar cuando se reciba el resultado… Para variar le pasamos una función anónima 🙂
  • Y la función anónima qué hace?
    • Pues bueno, primero vacía la combo indicada (llama al método empty() que lo que hace es vaciar el árbol DOM de un determinado elemento, por lo que aplicado a un <select> le elimina todas sus <option>).
    • Despues usa el método each de jQuery para iterar sobre la respuesta (asume que lo que se recibe es un array JSON). El método each recibe dos parámetros: el primero es la variable sobre la que iteramos y el segundo la función a ejecutar por cada elemento. En mi caso otra función anónima que usa el método append para añadir una etiqueta <option> a la combo (accediendo a las propiedades IdPais y Nombre de cada elemento del array JSON).

Bueno… hemos terminado la parte de cliente… veamos que debemos hacer en el servidor.

En primer lugar debemos crear la acción PaisesPorContinente en nuestro controlador: Una acción es un método público del controlador, y el nombre del método define el nombre de la acción, aunque esto se puede modificar con el uso del atributo ActionNameAttribute:

[ActionName("PaisesPorContinente")]
public ActionResult GetPaisesPorContinente(string id)
{
    var  paises = new PaisesModel().GetPaisesPorContinente(id);
    return new JsonResult() { Data = paises };
}

Accedemos al modelo y le pedimos que nos devuelva los paises del continente según el id indicado. Luego debemos devolver el resultado de la petición, que ahora no es código html, sinó código JSON. Por suerte la gente del framework MVC sabe que JSON se usa constantemente en AJAX, así que han implementado un serializador para convertir objetos de .NET en objetos JSON. Para devolver un objeto serializado en JSON simplemente devolved un JsonResult con la propiedad Data establecida al objeto que quereis devolver… y listos.

El código de mi modelo es muy simple:

public class Pais
{
    public string Continente { get; set; }
    public string Nombre { get; set; }
    public string IdPais { get; set; }
}

public class PaisesModel
{
    private List<Pais> paises;
    public PaisesModel()
    {
        this.paises = new List<Pais>();
        this.paises.Add(new Pais()
        {
            Continente = "eur",
            IdPais = "es",
            Nombre = "España"
        });
        // Más y más paises añadidos...
    }

    public IEnumerable<Pais> GetPaisesPorContinente
(string continente) { return this.paises.FindAll
(x => x.Continente == continente); } }

Simplemente devuelve una lista de objetos de la clase Pais. Fijaos como la clase Pais tiene las propiedades IdPais y Nombre que usábamos dentro de fillCombo! Y ya hemos terminado! Si quereis ver el proyecto completo, os lo podeis descargar de aquí.

Qué… fácil, no???

Un saludo a todos! 😉

pd: Por cierto, si vais a trabajar con combos y jQuery os recomiendo que le echeis un vistazo al plugin de jQuery para selectboxes, que añade funciones específicas para trabajar con selects (sí, sí… jQuery puede extenderse con plugins que le añaden todavía más funcionalidades!).

17 comentarios sobre “ASP.NET MVC y Ajax: fácil no, facilísimo (ii)”

  1. Hola…
    Me gustaria saber si me puedes ayudar, estoy haciendo un proyecto en MVC en donde tenga un campo de autoacompletar, pero aun no he logrado resultados.

    Muchas gracias además por este articulo tambien me servira de mucho ya que dependiendo de lo que seleccione en mi autoacompletado se actualizan unos combos…

    Saludos.

  2. Hola, estoy tratando de utilizar el menu con jquery que se encuentra en filament group www . filamentgroup .com /lab/jquery_ipod_style_and_flyout_menus/ pero he tenido problemas al utilizarlo porque no me despliega los contenidos, yo creo que esto se debe a la forma como se realizan los llamados al desplegar el contenido del menu.

    No tengo nada de experiencia en jquery por eso quisiera saber si me pudieras ayudar publicando alguna guia de como utilizar este menu en aspnet mvc o con algun consejo sobre como hacer que funcione…

  3. muy buen post!!! como deberia ser si fueran 3 combos?? poe ejem. continente–>pais–>ciudad.

    como ‘pais’ se actualiza mediante ajax, al intentar hacer actualizar ciudad desde este no pasa nada….

  4. intente descargar el ejemplo, pero no puedo el abrir el archivo que esta dentro del comprimido. podrias revisar si no esta dañado, porfas

  5. Hola

    No me esta funcionando la actualizacion del segundo combo, y copie el ejemplo que esta en la pagina

    No hace nada. por que puede ser??

    Gracias!!!

  6. @Cloudinamic.com

    Para que funcione el ejemplo tienes que cambiar la acción GetPaisesPorContinente en el controller

    Aquí copio el cambio que debes hacer. Saludos

    [ActionName(«PaisesPorContinente»)]
    public JsonResult GetPaisesPorContinente(string id)
    {
    var paises = new PaisesModel().GetPaisesPorContinente(id);
    return this.Json(paises, JsonRequestBehavior.AllowGet);
    }

  7. @Cloudinamic

    Buenas! Efectivamente si usas MVC2 debes aplicar el cambio que comentas. En MVC2 pusieron por defecto que los controladores no devolviesen datos en formato json a través de GET.

    El ejemplo es un poco viejo y estaba sobre MVC1 🙂

    Un saludo y gracias por tu comentario!!!!

  8. Buenos días.

    Antes que nada, agradecerte el artículo, ya que me ha ahorrado bastante tiempo y me ha hecho entender cómo funciona el intercambio de datos con JSON.

    Por otro lado, he tenido que cambiar la función Javascript fillCombo, pues aunque el combo se llenaba perfectamente, a posteriori no podía recuperar el valor seleccionado.

    Dónde pone $(«#»+updateId).append(«

  9. @Jonathan
    Totalmente cierto! Gracias por la corrección. Efectivamente, debería ser

    Saludos y gracias por comentar! 😉

  10. Hola, antes que nada gracias por tu aporte!.
    Me podrías ayudar con este problema?

    Yo tengo un menu dinamico ( se llena por base de datos ), y necesito que segun el item que seleccione del Menu, me muestre un sub menu.
    Actualmente estoy usando 2 divs, segun el click del div1, cargo el div2.
    En db, tengo 2 tablas:
    tabla menu
    tabla submenu ( con idPadre, asociado a Id de Menu ).

    espero puedan ayudarme.
    Muchas gracias!

  11. Muy buen post. Te felicito. Quisiera comentarte un problema que se me está presentado con una aplicación para ver si me puedes ayudar. Yo tengo una vista con un menú y cuando se ejecuta una de las opciones del menú llamo, mediante ajax, a otra vista. Cuando realizo el envío de la pagina, logicamente se invoca el controlador de la vista en su metodo post, se validan los datos y los errores encontrados me los muestra en la vista del controlador. Yo quisiera que esos mensajes de validaciones se muestren en la vista donde tengo el menú. No se si me he explicado bien. Me puedes ayudar?

  12. saludos excelente post lo vengo siguiendo desde el primer post, una preguntita mas sera posible para super completar este post coloques un grid con las opciones de actualizar, eliminar e insertar por celda utilizando asp.net mvc, acceso a datos y jquery. Seria perfecto así ya podría arrancar un proyecto que esta parado por esto.

  13. muy buen aporte…
    para mvc 3 también hay que usar lo que dice @Cloudinamic.com de mvc 2… jaja me estaba volviendo loco buscando hasta que lei ese comentario… gracias por todo me ayudo un monton tu nota!

    @Cloudinamic.com decía:

    Para que funcione el ejemplo tienes que cambiar la acción GetPaisesPorContinente en el controller

    Aquí copio el cambio que debes hacer. Saludos

    [ActionName(«PaisesPorContinente»)]

    public JsonResult GetPaisesPorContinente(string id)

    {

    var paises = new PaisesModel().GetPaisesPorContinente(id);

    return this.Json(paises, JsonRequestBehavior.AllowGet);

    }

Responder a anonymous Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *