Formularios web AJAX en ASP.NET MVC 5 - Ajax Helpers

En el anterior Post de este Bolg, Formularios web AJAX con jQuery en ASP.NET Core MVC, vimos como implementar AJAX en formularios web Http POST mediante jQuery, para aplicaciones ASP.NET Core MVC.

En este artículo, veremos cómo sería desarrollar el mismo ejemplo anterior, pero esta vez en una aplicación ASP.NET MVC 5, utilizando el potencial y la transparencia a nivel de desarrollo que nos ofrecen los Html Helpers y Ajax Helpers del full Framework ASP.NET MVC >= 4.5.

Creando el proyecto

Para este ejemplo, utilizaremos Visual Studio 2017 para crear un nuevo proyecto Web MVC, con plataforma de destino .NET Framework 4.6.1 (>= 4.5).

La aplicación de ejemplo consistirá en un sencillo sistema de Blog, en el cual, a través de un formulario, introduciremos Posts que serán enviados al Servidor para ser almacenados en una Base de Datos. Además, cada vez que creemos un nuevo Post, se actualizará una lista de Posts en pantalla con el nuevo contenido de la Base de Datos. 

Nota: Todo este proceso lo haremos sin enviar en ningún momento la pagina completa al Servidor, o sea, mediante AJAX enviaremos solo los datos y recibiremos solo la información necesaria para actualizar parcialmente la página.

Blog Post example

El Modelo de datos

En primer lugar crearemos la clase Post.cs que será el Modelo de datos principal de la aplicación.

    public class Post
    {
        public Post()
        {
            this.Id = Guid.NewGuid();
            this.Fecha = DateTime.Now;
        }

        [Required]
        [Key]
        public Guid Id { get; set; }

        [Required]
        public DateTime Fecha { get; set; }

        [StringLength(50)]
        [Required(ErrorMessage ="Este campo es obligatorio.")]
        [RegularExpression(@"^[A-Z a-z0-9ÑñáéíóúÁÉÍÓÚ\\-\\_]+$",
            ErrorMessage = "El Nombre debe ser alfanumérico.")]
        [Display(Name = "Nombre")]
        public string Nombre { get; set; }

        [RegularExpression(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*",
            ErrorMessage = "Dirección de Correo electrónico incorrecta.")]
        [StringLength(50)]
        [Display(Name = "Correo electrónico")]
        public string Email { get; set; }

        [StringLength(100)]
        [Required(ErrorMessage = "Este campo es obligatorio.")]
        [Display(Name = "Título")]
        public string Titulo { get; set; }

        [StringLength(1500)]
        [Required(ErrorMessage = "Este campo es obligatorio.")]
        [DataType(DataType.MultilineText)]
        [Display(Name = "Comentario")]
        public string Comentario { get; set; }
    }

También crearemos la clase auxiliar PostViewModel.cs, que contendrá los datos del Modelo que posteriormente serán enviados a la Vista principal de la aplicación.

    public class PostViewModel
    {
        public Post Post { get; set; }
        public List<Post> PostList { get; set; }
    }

El Controlador

A continuación crearemos el Controlador BlogController.cs. En este incluiremos dos Acciones básicas, que gestionarán las peticiones GET y POST enviadas desde el explorador.

    public class BlogController : Controller 
    {
        private ApplicationDbContext _dbContext;

        // GET: Blog/Post
        [HttpGet]
        public ActionResult Post()
        {
            using (_dbContext = new ApplicationDbContext())
            {
                var _model = new PostViewModel()
                {
                    Post = new Post(),
                    PostList = _dbContext.Posts.OrderByDescending(x => x.Fecha).ToList()
                };
                return View(_model);
            };
        }

        // POST: Blog/Post
        [HttpPost]
        public ActionResult Post(Post post)
        {
            using (_dbContext = new ApplicationDbContext())
            {
                if (ModelState.IsValid)
                {
                    _dbContext.Posts.Add(post);
                    _dbContext.SaveChanges();
                }

                return PartialView("_PostListPartial",
                        _dbContext.Posts.OrderByDescending(x => x.Fecha).ToList());
            }
        }
    }

Nota: Como podemos ver, el Controlador hace referencia a un objeto privado del tipo ApplicationDbContext (Contexto de datos de Entity Framework). Para más información acerca de cómo implementar un DbContext de Entity Framework en ASP.NET MVC, pueden consultar el siguiente Post: Entity Framework 6 y Sql Server Compact CE en ASP.NET MVC 5.

La Acción GET

public ActionResult Post(): Aquí crearemos un nuevo objeto del tipo PostViewModel, cuya propiedad PostList contendrá una lista ordenada de todos los Posts existentes en la Base de Datos. La Acción devolverá la Vista asociada (Post.cshtml) con el Modelo de datos anteriormente creado.

La Acción POST

public ActionResult Post(Post post): Aquí recibiremos como parámetro un objeto del tipo Post con los datos enviados desde el formulario. Si los datos recibidos son válidos, guardaremos el Post en la Base de Datos, y devolveremos una Vista parcial (_PostListPartial.cshtml) con una lista ordenada de todos los Posts existentes en la Base de Datos.

La Vista

Antes de implementar la Vista principal de la aplicación, crearemos la Vista parcial _PostListPartial.cshtml. Está se encargará de construir una lista con los Posts existentes en la Base de Datos.

@model  List<Post>

@foreach (var post in Model)
{
    <div class="row">
        <div class="col-md-12">
            <div>
                <strong>@post.Fecha.ToShortDateString() 
                        @post.Fecha.ToLongTimeString()</strong>&nbsp;
                <span>@post.Nombre</span>&nbsp;
                <span>@post.Email</span>
            </div>
            <strong>@post.Titulo</strong>
            <p>
                @post.Comentario
            </p>
            <hr />
        </div>
    </div>
}

Por último solo nos quedaría crear la Vista principal Post.cshtml. Esta es realmente la parte más importante de nuestra aplicación de ejemplo, ya que es aquí donde implementaremos el Ajax Helper y el código JavaScript encargado de establecer y gestionar el flujo de datos entre el explorador del usuario y el Controlador de la aplicación.

Implementando el formulario AJAX - @Ajax.BeginForm()

Antes de ver cómo crear la Vista principal, analizaremos brevemente el funcionamiento del Ajax Helper @Ajax.BeginForm(), el cual será el encargado de habilitar AJAX en nuestro formulario web.

Una plantilla estándar de este Helper, sería la siguiente:

@using (Ajax.BeginForm( "Action", "Controller", 
                        new { RouteValue = Value },
                        new AjaxOptions
                        {
                            HttpMethod = "Post",
                            OnBegin = "onBeginSubmit",
                            OnComplete = "onCompleteSubmit",
                            OnFailure = "onFailureResult",
                            OnSuccess = "onSuccessResult",
                            UpdateTargetId = ".....",
                            InsertionMode = InsertionMode.Replace
                        },
                        new { id = "AjaxForm" }))
{
    // ...
    // AQUÍ EL CUERPO DEL FORMULARIO WEB
    // ...
}

Como vemos, @Ajax.BeginForm() recibe como parámetro un objeto del tipo AjaxOptions, en el cual configuraremos a través de sus propiedades el comportamiento que tendrá el formulario web AJAX.

Si observáramos el código fuente HTML que genera este Ajax Helper, veríamos que se ha generado una etiqueta del tipo <form />, con una serie de atributos del tipo data-ajax-....=.

<form action="/Controller/Action" 
      data-ajax="true" 
      data-ajax-begin="onBeginSubmit" 
      data-ajax-complete="onCompleteSubmit" 
      data-ajax-failure="onFailureResult" 
      data-ajax-method="Post" 
      data-ajax-success="onSuccessResult" 
      id="AjaxForm" 
      method="post">
 
     // ...
     // AQUÍ EL CUERPO DEL FORMULARIO WEB
     // ...

</form>

Esto es debido a que el Helper @Ajax.BeginForm(), solo funciona en conjunción a la librería jQuery jquery.unobtrusive-ajax.js, la cual será la encargada de interpretar los atributos data-ajax-....= del <form /> para darle la funcionalidad AJAX requerida.

Instalando jQuery unobtrusive Ajax

Desde la consola de administración de paquetes NuGet (Herramientas > Administrador de paquetes NuGet > Consola del Administrador de paquetes), ejecutaremos el siguiente comando:

PM> Install-Package Microsoft.jQuery.Unobtrusive.Ajax -Version 3.2.6

Una vez finalizada la instalación, comprobaremos que en la carpeta Scripts de nuestro proyecto se han creado correctamente los Scripts jquery.unobtrusive.ajax*.

jquery-unobtrusive-ajax

En los proyectos ASP.NET MVC 5, los Scripts que posteriormente insertaremos en nuestras Vistas, deben ser definidos con anterioridad en el archivo de configuración BundleConfig.cs, dentro de la carpeta App_Start.

Siguiendo esta convención, añadiremos el siguiente código a la clase BundleConfig.cs:

    public class BundleConfig
    {
        // Para obtener más información sobre las uniones, visite 
        // https://go.microsoft.com/fwlink/?LinkId=301862
        public static void RegisterBundles(BundleCollection bundles)
        {
            // ...

            // JQUERY UNOBTRUSIVE PARA @Ajax.BeginForm() 
            bundles.Add(new ScriptBundle("~/bundles/jqueryajax").Include(
                        "~/Scripts/jquery.unobtrusive*"));
           
            // ...
        }
    }

Posteriormente, en nuestra Vista principal, "renderizaremos" estos Scripts dentro de la sección @section scripts {...} de la siguiente manera:

@section scripts {

    // ...

    @*JQUERY UNOBTRUSIVE PARA Ajax.BeginForm()*@
    @Scripts.Render("~/bundles/jqueryajax")

    // ...

}

La Vista principal

Llegados a este punto, ya podemos crear la Vista Post.cshtml. El código sería el siguiente:

@model PostViewModel

<h3>BlogPost</h3>

@using (Ajax.BeginForm("Post", "Blog", null,
                        new AjaxOptions
                        {
                            HttpMethod = "Post",
                            OnBegin = "onBeginSubmit",
                            OnComplete = "onCompleteSubmit",
                            OnFailure = "onFailureResult",
                            OnSuccess = "onSuccessResult",
                            UpdateTargetId = "PostList",
                            InsertionMode = InsertionMode.Replace
                        },
                        new { id = "AjaxForm" }))
{
    <div class="row">
        <div class="col-md-6">
            <div class="form-group">
                @Html.TextBoxFor(m => m.Post.Nombre,
                    new { @class = "form-control input-sm", id = "Nombre", placeholder = "Nombre" })
                @Html.ValidationMessageFor(m => m.Post.Nombre, null, new { @class = "text-danger" })
            </div>
        </div>

        <div class="col-md-6">
            <div class="form-group">
                @Html.TextBoxFor(m => m.Post.Email,
                    new { @class = "form-control input-sm", id = "Email", placeholder = "Email" })
                @Html.ValidationMessageFor(m => m.Post.Email, null, new { @class = "text-danger" })
            </div>
        </div>

        <div class="col-md-12">
            <div class="form-group">
                @Html.TextBoxFor(m => m.Post.Titulo,
                    new { @class = "form-control input-sm", id = "Titulo", placeholder = "Titulo" })
                @Html.ValidationMessageFor(m => m.Post.Titulo, null, new { @class = "text-danger" })
            </div>
        </div>

        <div class="col-md-12">
            <div class="form-group">
                @Html.TextAreaFor(m => m.Post.Comentario,
                    new { rows = 4, @class = "form-control", id = "Comentario", placeholder = "Comentario" })
                @Html.ValidationMessageFor(m => m.Post.Comentario, null, new { @class = "text-danger" })
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-9">
            <div id="ErrorAlert" class="alert alert-danger" style="display:none" role="alert">
                Error en los datos enviados!
            </div>
            <div id="ExitoAlert" class="alert alert-success" style="display:none" role="alert">
                Datos recibidos correctamente!
            </div>
        </div>
        <div class="col-md-3">
            <div class="form-group float-right">
                <img id="AjaxLoader" alt="Enviando ..." style="display:none" src="~/Images/loading.gif" />
                &nbsp;&nbsp;
                <button id="SubmitBtn" type="submit" class="btn btn-primary">
                    <i class="glyphicon glyphicon-send"></i>
                    &nbsp;Enviar datos
                </button>
            </div>
        </div>
    </div>
}

<h3>PostList</h3>
<div id="PostList">
    @Html.Partial("_PostListPartial", Model.PostList)
</div>

@section scripts {

    <!-- JQUERY UNOBTRUSIVE PARA Ajax.BeginForm() -->
    @Scripts.Render("~/bundles/jqueryajax")

    <!-- JQUERY UNOBTRUSIVE PARA VALIDACIÓN -->
    @Scripts.Render("~/bundles/jqueryval")

    <!-- AL COMENZAR EL SUBMIT -->
    <script>
        var onBeginSubmit = function () {
            // Mostramos el Ajax Loader
            $("#AjaxLoader").show("fast");

            // Deshabilitamos el botón de Submit
            $("#SubmitBtn").prop("disabled", true);
        };
    </script>

    <!-- AL FINALIZAR EL SUBMIT -->
    <script>
        var onCompleteSubmit = function () {

        };
    </script>

    <!-- SE EJECUTA SI TODO FUE BIEN -->
    <script>
        var onSuccessResult = function () {
            // Un pequeño esfecto especial ;)
            $("#PostList .row").first().hide();
            $("#PostList .row").first().slideToggle("slow");

            // Limpia el formulario
            $("#Comentario").val("");
            $("#Nombre").val("");
            $("#Email").val("");
            $("#Titulo").val("");
            // Otra forma de limpiar el Formulario
            //$("#AjaxForm")[0].reset();
            //$("#AjaxForm").trigger('reset');

            // Escondemos el Ajax Loader
            $("#AjaxLoader").hide("slow");

            // Habilitamos el botón de Submit
            $("#SubmitBtn").prop("disabled", false);

            // Mostramos un mensaje de éxito.
            $("#ExitoAlert").show("slow").delay(2000).hide("slow");
        };
    </script>

    <!-- SE EJECUTA SI SE PRODUJO UN ERROR -->
    <script>
        var onFailureResult = function () {
            // Mostramos un mensaje de error.
            $("#ErrorAlert").show("slow").delay(2000).hide("slow");

            // Escondemos el Ajax Loader
            $("#AjaxLoader").hide("slow");

            // Habilitamos el botón de Submit
            $("#SubmitBtn").prop("disabled", false);
        };
    </script>

} 

La Librería jQuery

Si observamos el código de la Vista Post.cshtml, nos daremos cuenta de que no hemos hecho referencia en ningún momento a la librería JavaScript principal de jQuery (jquery-x.x.x.min.js). Esto se debe a que Visual Studio la incluye en la página maestra o Layout (_Layout.cshtml) cuando creamos un nuevo proyecto web ASP.NET MVC.

Nota: También incluye por defecto en la página maestra (Layout) las librerías Bootstrap, hojas de estilo y JavaScripts de la aplicación.

El "Binding" de datos

Como podemos ver, los identificadores "id=" de los Html Helpers (@Html.TextBoxFor(...)) que generarán las etiquetas <input /> encargadas de capturar los datos del formulario, coinciden exactamente con las propiedades del Modelo de datos de la aplicación.

Esto es absolutamente necesario para que la Acción ActionResult Post(Post post) del Controlador, pueda reconocer mediante el mecanismo de Binding de ASP.NET MVC, que los datos recibidos hacen referencia al objeto complejo del Modelo de datos Post.cs

La validación de datos del formulario

Un punto importante en cualquier formulario web, es la validación de los datos que enviamos al Servidor. 

Cuando creamos un proyecto web ASP.NET MVC 5 con Visual Studio, este crea una una entrada en el archivo de configuración BundleConfig.cs, que hace referencia a las librerías jQuery de validación jquery.validate.*.

public class BundleConfig
    {
        // Para obtener más información sobre las uniones, visite 
        // https://go.microsoft.com/fwlink/?LinkId=301862
        public static void RegisterBundles(BundleCollection bundles)
        {
            // ...

            // JQUERY UNOBTRUSIVE PARA VALIDACIÓN
            bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.validate*"));
           
            // ...
        }
    }

Como vimos anteriormente en el código de la Vista principal de la aplicación Post.cshtml, bastaría con hacer referencia a estos Scripts de validación dentro de la sección @section scripts {...} de la siguiente manera:

@section scripts {

    // ...

    <!-- JQUERY UNOBTRUSIVE PARA VALIDACIÓN -->
    @Scripts.Render("~/bundles/jqueryval")

    // ...

}

Nota: En el caso de querer instalar las librerías de validación de forma manual, podemos hacerlo a través de la consola del administrador de paquetes NuGet, de la siguiente manera:

PM> Install-Package Microsoft.jQuery.Unobtrusive.Validation -Version 3.2.11

Por último, tener en cuenta dos cosas:

La primera es que también es necesario incluir para cada campo del formulario a validar, su correspondiente Html Helper @Html.ValidationMessageFor(...).

Y la segunda es que en el archivo Web.config de la aplicación, debemos tener marcadas a True las variables de configuración ClientValidationEnabledUnobtrusiveJavaScriptEnabled.

<appSettings>
    ...
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    ...
</appSettings>

Nota: Para más información acerca de la validación de formularios en ASP.NET MVC, pueden consultar el Post Validación de formularios en ASP.NET MVC - Unobtrusive Validate.

 

   EtiquetasASP.NET .NET MVC AJAX jQuery

  Compartir


  Nuevo comentario

El campo Comentario es obligatorio.
El campo Nombre es obligatorio.

Enviando ...

  Comentarios

Mr Marvel Mr Marvel

Muy bueno, saludos desde trujillo, Perú.
Rafael Acosta Administrador Rafael Acosta

@Mr Marvel: Gracias por tu comentario. Compartir los artículos en la redes sociales es fundamental para que este Blog siga ofreciendo publicaciones de calidad y en español.


Juan Juan

gracias

Perfil para Rafael Acosta en Stack Overflow en español, preguntas y respuestas para programadores y profesionales de la informática.

  Etiquetas

.NET Core .NET Framework .NET MVC .NET Standard AJAX ASP.NET ASP.NET Core ASP.NET MVC Bootstrap Buenas prácticas C# Cookies Entity Framework Gráficos JavaScript jQuery JSON JWT PDF Pruebas Unitarias Seguridad SEO SOAP Sql Server SqLite Swagger Validación Web API Web Forms Web Services WYSIWYG

  Nuevos


  Populares















Utilizamos cookies propias y de terceros para mejorar nuestros servicios y ofrecerle una mejor experiencia de navegación. Si continúa navegando consideramos que acepta su uso. Más información   Acepto