martes, 30 de junio de 2009

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!).


pd: Este post es (para variar) un crosspost desde mi blog de geeks.ms!

No hay comentarios: