jQuery 1.9 y el “unobtrusive ajax” de ASP.NET MVC

Publicado 18/1/2013 12:08 por Eduard Tomàs i Avellana

Hace justo casi nada que ha salido la nueva versión de jQuery 1.9 y he aprovechado para actualizar una aplicación web que tenía a medias.

Pues bien, si actualizas una aplicación ASP.NET MVC que use unobtrusive ajax y actualizas a ASP.NET MVC… deja de funcionar. Pero vayamos por partes…

Reproducción del problema

Abre VS2012 y crea un nuevo proyecto ASP.NET MVC4. Usa la plantilla Basic (no la Empty). Podemos ver como por defecto nos ha agregado, entre otros, el fichero jQuery-unobstrusive-ajax. Este fichero es el que da soporte para el unobtrusive ajax de MVC. Ya hablé en este blog hace un tiempecillo sobre el unobstrusive ajax de MVC.

Para ver el problema en acción, añadimos un controlador HomeController con la acción Index por defecto y una acción que he llamado PostData:

public class HomeController : Controller

{

    public ActionResult Index()

    {

        return View();

    }

    [HttpPost]

    public ActionResult PostData(string name)

    {

        ViewBag.DataName = name;

        return PartialView();

    }

}

La vista Index.cshtml es muy sencilla, tiene tan solo un Ajax.BeginForm:

@using (Ajax.BeginForm("PostData", new AjaxOptions() {HttpMethod = "Post", UpdateTargetId = "datadiv"}))

{

 

    <label for="name">Name: </label>

    <input type="text" name="name" id="name"/>

    <input type="submit" value="Send" />

}

<hr />

Aquí irá el resultado: <p />

<div id="datadiv">

</div>

Y finalmente nos queda la vista PostData.cshtml que contiene el código HTML que se incrustará en el div “datadiv”:

<h2>Hola @ViewBag.DataName</h2>

Tnemos que hacer una última modificación a la página de _Layout.cshtml que es añadir el bundle que nos añade (entre otros) jQuery-unobstrusive-ajax.js. Para ello añadimos la siguiente línea justo antes del RenderSection:

@Scripts.Render("~/bundles/jqueryval")

Listos, con esto tenemos un formulario que es enviado via Ajax al servidor y el resultado (la vista PostData.cshtml) se incrusta en el formulario.

Ahora simplemente reemplazamos el fichero de jQuery 1.7.1 por el de jQuery 1.9.0 y volvemos a probar la aplicación.

Veréis que el formulario no se envía por Ajax (en su lugar se envía por un POST normal) por lo que el resultado de PostData.cshtml no se incrusta en el div si no que pasa a ser toda la nueva página.

La causa del error y la solución

La causa es que jquery-unobtrusive-ajax.js que es quien se encarga de dar soporte al unobtrusive ajax de ASP.NET MVC usa el método live de jQuery. Pero dicho método fue declarado obsoleto en jQuery 1.7 y se ha eliminado en 1.9. Dicho método permitía asociarse a un evento de cualquier elemento del DOM actual o futuro.

El método que debe usarse actualmente en lugar de live es el método on. No obstante la sintaxis es un poco distinta, ya que el método on tiene más usos en jQuery.

$("form[data-ajax=true]").live("submit", function (evt) {

Vamos a modificar la llamada a “live” a una llamada a “on”. Para que “on” actue como live debemos pasarle a “on” 3 parámetros:

  • El evento (igual que live, será “submit”)
  • Un selector (elementos “hijos”) del selector base que es el que debe existir siempre
  • La función gestora (igual que live).

En este caso nuestra línea queda como:

 $("body").on("submit", "form[data-ajax=true]",function (evt) {

Fijaos: he movido el selector al segundo parámetro de “on” y he puesto como selector base “body” (no es el más óptimo, pero así estoy seguro de que existe siempre). La idea es que la función pasada se asocia a todos los elementos presentes y futuros de tipo “form[data-ajax=true]” que estén dentro del selector base (body).

Para el resto de llamadas a live (hay 3 más) hacemos la misma sustitución que nos quedarán así:

$("body").on("click", "form[data-ajax=true] :submit", function (evt) {

$("body").on("click", "a[data-ajax=true]", function (evt) {

$("body").on("click", "form[data-ajax=true] input[type=image]", function (evt) {

Y listos! Con esto ya hemos recuperado la funcionalidad del unobtrusive ajax de MVC y nuestra aplicación debería volver a funcionar correctamente!

Saludos!

PD: Actualmente el fichero jquery-unobstrusive-ajax.js se distribuye a través de un paquete de NuGet llamado Microsoft jQuery Unobtrusive Ajax. Es de esperar que en breve lo actualizarán ;-)

Archivado en:
Comparte este post:

Comentarios

# re: jQuery 1.9 y el “unobtrusive ajax” de ASP.NET MVC

Friday, January 18, 2013 4:53 PM by Juanma

Son las típicas cosas que hacen que tengas que pensarte muy mucho antes de actualizar componentes, gracias por explicar cómo solucionarlo.

Por cierto, creo que te puedes ahorrar el selector al usar "on" y llamarlo directamente así:

$("form[data-ajax=true] :submit").on("click", function(evt) {...}

Más info (y spam descarado :) ) aquí: blog.koalite.com/.../eventhandlers-con-jquery-1-7-on-vs-click

# re: jQuery 1.9 y el “unobtrusive ajax” de ASP.NET MVC

Friday, January 18, 2013 10:27 PM by Eduard Tomàs i Avellana

Buenas Juanma!

Pues me haces dudar: si no paso el selector on() se comporta más como bind() que como live() no? Es decir se suscribirá al evento solo si el formulario existe cuando se ejecuta el código no?

Y el código de jquery-unobtrusive-ajax.js se encuentra todo él dentro de una función anónima autoinvocada, por lo que se ejecutará al acto. Es cierto que se suele poner al final del <body> por lo que el DOM puede (repito puede, no debe) estar listo... Pero si tenemos actualizaciones por ajax que creen formularios (o bien que el DOM NO esté listo cuando se ejecuta el código de jquery-unobtrusive-ajax.js) diría que si no pongo el selector en el .on() estos nuevos formularios no serían procesados.

Saludos!

# re: jQuery 1.9 y el “unobtrusive ajax” de ASP.NET MVC

Tuesday, January 22, 2013 1:31 PM by Juanma

Hola,

Tienes razón, si los elemenos se crean dinámicamente no te puedes ahorrar el selector, entre otras cosas porque en ese caso el $("form..") no encontraría nada y no podrías asociarle el evento. Ahorrarte el selector sólo es posible si los elementos ya existen en el momento de invocar la función.

Gracias por la aclaración.