AJAX y ASP.NET MVC

Cuando creamos un proyecto de MVC, de forma automática se genera una carpeta llamada Scripts con los siguientes archivos js:

En el momento que queramos hacer uso de ellos, únicamente debemos importarlos, generalmente en la Master Page de nuestra aplicación:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>
<asp:ContentPlaceHolder ID="TitleContent" runat="server" />
</title>
<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />

<script src="../../Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>

<script src="../../Scripts/MicrosoftAjax.js" type="text/javascript"></script>

<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>

</head>

Para mostrar un ejemplo de la forma de trabajar con Ajax en una aplicación MVC, voy a llevar a cabo una serie de pasos para ajustar el contenido de la aplicación Movie Manager usada en otros ejemplos.

PARTIAL VIEW DEL LISTADO DE PELÍCULAS

Selecionamos la carpeta Movie, dentro de Views, y con lo el botón derecho seleccionamos Add => View. Especificamos la siguiente configuración:

Pulsando en Add, se genera una nueva vista con extensión ascx. He modificado ligeramente el contenido para configurar las acciones de detalle, edición y una última para la eliminación de películas de manera asíncrona con Ajax. Además, he añadido tres funciones utilizando JQuery.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<MovieManager.Models.Objects.Movie>>" %>
<%@ Import Namespace="MovieManager.Helpers" %>

<script type="text/javascript">

function beginMovieList(args) {
$('#divMovieList').slideUp('normal');
}
function successMovieList() {
$('#divMovieList').slideDown('normal');
}
function failureMovieList() {
alert("Could not retrieve movies.");
}

</script>

<table>
<tr>
<th>
</th>
<th>
Id
</th>
<th>
Name
</th>
<th>
Genre
</th>
<th>
Synopsis
</th>
<th>
Date
</th>
</tr>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%=Html.ImageLink("Movie", "Details", "Content\detail.gif", new {item.Id })%>
|
<%=Html.ImageLink("Movie","Edit","Content\edit.gif",new {item.Id }) %>
|
<%= Ajax.ActionLink("Delete", "Delete", new { id = item.Id }, new AjaxOptions { Confirm = "Delete movie?", HttpMethod = "Delete", UpdateTargetId = "divMovieList", OnBegin = "beginMovieList", OnComplete = "successMovieList", OnFailure = "failureMovieList" })%>
</td>
<td>
<%= Html.Encode(item.Id) %>
</td>
<td>
<%= Html.Encode(item.Name) %>
</td>
<td>
<%= Html.Encode(item.Genre) %>
</td>
<td>
<%= Html.Encode(item.Synopsis) %>
</td>
<td>
<%= Html.Encode(String.Format("{0:g}", item.Date)) %>
</td>
</tr>
<% } %>
</table>

En la acción Delete se ha utilizado el helper específico para los controles Ajax. Dentro del mismo, he usado ActionLink donde se usan los mismos parámetros que en un control normal del Html helper a excepción del último parámetro: AjaxOptions. Este objeto, contiene una serie de propiedades para su configuración. Las utilizadas en el ejemplo son:

  1. Confirm: Antes de realizar la acción, se tendrá que confirmar la misma a través de un cuadro de diálogo con el texto introducido en esta propiedad. 
  2. HttpMethod:Se indica el tipo de verbo http que se utilizará en la llamada. En esta ocasión se hace uso de Delete ya que estamos intentando eliminar un recurso.
  3. UpdateTargetId: Id del elemento de nuestra vista que se va a actualizar, como si se tratara de un Update Panel.
  4. OnBegin: Se le pasa el nombre de la función que se ejecutará cuando se inicie la llamada asíncrona. Por ejemplo, podemos llamar a la función beginMovieList para ocultar el listado de películas cuando comience la eliminación de una de ellas.
  5. OnComplete: Si la llamada asíncrona se completa con éxito, se lanzará la función asociada a esta propiedad. En este caso successMovieList volverá a mostrar el listado de películas actualizado.
  6. OnFailure: Si durante la llamada se produce algún tipo de error, se lanzará la función enlazada a esta propiedad en vez de la función añadida en OnComplete, pudiendo avisar al usuario de la incidencia.

MODIFICAR LA VISTA INDEX

Para poder hacer uso de la partial view, y  no perder la funcionalidad hasta ahora conseguida, debemos modificar la vista Movies/Index.aspx de la siguiente manera:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<MovieManager.Models.Objects.Movie>>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
Index</h2>
<div id="divMovieList">
<% Html.RenderPartial("MovieList"); %>
</div>
<p>
<%= Html.ActionLink("Create New", "Create") %>
</p>
</asp:Content>

Se ha añadido un elemento de tipo div, el cual contiene una llamada a Html.RenderPartial que renderizará la vista parcial que acabamos de crear.
En MovieController creamos una nueva acción para eliminar la película seleccionada.

[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult Delete(int id)
{
var movieToDelete = _movieRepository.GetMovie(id);
_movieRepository.Delete(movieToDelete);
return PartialView("MovieList", _movieRepository.ListMovies());
}

Cuando recupera la pelicula a través de NHibernate y elimina la misma, retorna el nuevo listado de películas a MovieList.

Los métodos Delete y GetMovie han sido añadidos tanto en la interfaz IMovieRepository como en su implementación, siempre en la parte del Modelo.

using System.Collections.Generic;
using MovieManager.Models.Objects;

namespace MovieManager.Models
{
public interface IMovieRepository
{
IList<Movie> ListMovies();
void SaveMovie(Movie movieToSave);
Movie GetMovie(int id);
void Delete(Movie movieToDelete);
}
}

using System.Collections.Generic;
using MovieManager.Models.Objects;
using NHibernate;
using NHibernate.Criterion;

namespace MovieManager.Models
{
public class MovieRepository : IMovieRepository
{
private readonly ISessionFactory _session;

public MovieRepository(ISessionFactory sessionFactory)
{
_session = sessionFactory;
}

public IList<Movie> ListMovies()
{
return _session.GetCurrentSession().CreateCriteria(typeof(Movie)).List<Movie>();
}

public void SaveMovie(Movie movieToSave)
{
_session.GetCurrentSession().SaveOrUpdate(movieToSave);
}

public Movie GetMovie(int id)
{
return (Movie)_session.GetCurrentSession().CreateCriteria(typeof(Movie))
.Add(Restrictions.Like("Id", id)).UniqueResult();
}

public void Delete(Movie movieToDelete)
{
_session.GetCurrentSession().Delete(movieToDelete);
}
}
}

Como hemos visto, podemos trabajar de una forma más simplificada con Ajax, gracias a MVC y JQuery, adaptando la aplicación de una forma poco agresiva 🙂

Adjunto el proyecto por si fuera de utilidad.

¡Saludos!

13 comentarios sobre “AJAX y ASP.NET MVC”

  1. Muchas gracias por tu comentario JTorrecilla.

    Si tienes alguna petición, sugerencia al respecto, por favor no dudes en ponerte en contacto conmigo 😉

    ¡Saludos!

  2. Gis,

    Cual es tu opinión acerca de la libreria Ajax de MS? Hay gente que comenta que es muy pesada y que de hecho no hace nada que no se pueda hacer con jQuery, y recomiendan usar solo jQuery.

    Contando que jQuery ya viene incluída con el project template por defecto de MVC, hay alguna razón para utilizar / no utilizar la librería de ajax de MS?
    Entiendo que se pierde toda la funcionalidad por defecto incluída en el AjaxHelper, pero no debería ser especialmente complejo recrearla…

    Que opinas sobre esto??? 😉

  3. @JTorrecilla
    Sí, sí… eso ya lo decía en mi comentario 🙂
    Mi pregunta va más en «porque usar la librería de ajax de MS existiendo jQuery y que esta viene incorporada en las plantillas por defecto»?

    Gracias por tu respuesta!!!! 😉
    Un abrazo!

  4. Gracias por vuestros comentarios =)

    @Eduard, por lo poco que he visto hasta ahora de MS Ajax, no me invita a reemplazar JQuery por esta la verdad…
    Quizás los chicos de Microsoft en este aspecto han dado también más juego a JQuery asumiendo la librería como un estándar de las plantillas MVC, integración con VS 2010,etc.
    Me gustaría poder echarle un vistazo más detenidamente pero ya te digo, por lo pronto no se me ocurre una buena razón que las diferencie a favor de MS Ajax.

    ¡Saludos!

  5. Hola Nachetex,

    Gracias por tu comentario 🙂 Espero volver a tocar algún día Java pero, por el momento, mi mayor parte del tiempo se la dedico a .NET que es con lo que trabajamos.

    ¡Ojalá pudiera abarcar más! 🙂

    ¡Saludos!

Responder a gtorres Cancelar respuesta

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