Formularios web AJAX en ASP.NET Web Forms - UpdatePanel

En el anterior artículo de este Blog Formularios web AJAX con jQuery en ASP.NET Core MVC, vimos cómo implementar un formulario web Http POST en ASP.NET Core MVC, que utilizaba jQuery AJAX para enviar datos al servidor, y a su vez, recibía de manera asíncrona una respuesta HTML que le permitía actualizar la información visual en la misma página.

En este primer artículo del 2020, me gustaría recordar lo fácil e intuitivo que resultaba implementar esta misma funcionalidad AJAX en ASP.NET Web Forms, utilizando solamente los controles <asp:UpdatePanel /><asp:ScriptManager />.

Creando el proyecto

Para este ejemplo, utilizaremos Visual Studio 2019 para crear un nuevo proyecto Web ASP.NET Web Forms, con plataforma de destino .NET Framework 4.7.1 .

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.

form-ajax-web-forms

Realizando el ejemplo de manera síncrona - Postback

Antes de implementar la funcionalidad AJAX en nuestra aplicación de ejemplo, realizaremos el desarrollo de la misma utilizando el mecanismo estándar (síncrono) que provee ASP.NET Web Forms por defecto, o sea, los Postbacks.

Lo haremos así, para posteriormente ver lo fácil que es reutilizar el código ya existente y transformarlo en asíncrono (AJAX), sin escribir ni una sola línea de código en JavaScript.

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;
        }

        public Guid Id { get; set; }
        public DateTime Fecha { get; set; }
        public string Nombre { get; set; }
        public string Email { get; set; }
        public string Titulo { get; set; }
        public string Comentario { get; set; }
    }

La página ASPX

Seguidamente, crearemos un nuevo Formulario Web Forms (página ASPX) con el nombre Blog.aspx. En esta página implementaremos toda la funcionalidad del ejemplo. 

La página constará de un formulario web donde introduciremos los datos (Post) que serán enviados al Servidor. También tendremos una lista paginada que mostrará los registros (Posts) que ya están almacenados en la Base de Datos.

El código sería el siguiente:

<%--FORMULARIO PARA ENVÍO DE DATOS--%>
<h3>BlogPost</h3>

<div class="row">
    <div class="col-md-6">
        <div class="form-group">
            <asp:TextBox runat="server" ID="idNombre" CssClass="form-control input-sm" 
                placeholder="Nombre"></asp:TextBox>
        </div>
    </div>
    <div class="col-md-6">
        <div class="form-group">
            <asp:TextBox runat="server" ID="idEmail" CssClass="form-control input-sm" 
                placeholder="Email" TextMode="Email" ></asp:TextBox>
        </div>
    </div>
    <div class="col-md-12">
        <div class="form-group">
            <asp:TextBox runat="server" ID="idTitulo" CssClass="form-control input-sm" 
                placeholder="Titulo"></asp:TextBox>
        </div>
    </div>
    <div class="col-md-12">
        <div class="form-group">
            <asp:TextBox runat="server" ID="idComentario" CssClass="form-control input-sm" 
                placeholder="Comentario" TextMode="MultiLine" Rows="5"></asp:TextBox>
        </div>
    </div>
</div>

<div class="row">
    <div class="col-md-6">
        <div class="form-group float-right">                            
            <asp:Button runat="server" ID="idSubmitBtn" OnClick="idSubmitBtn_Click" 
                CssClass="btn btn-primary" Text="Enviar Datos"/>                            
        </div>
    </div>
</div>


<%--LISTA DE POSTS ENVIADOS AL SERVIDOR --%>
<h3>PostList</h3>

<%--Control de servidor ListView--%>
<asp:ListView runat="server" ID="idListView" 
                ItemPlaceholderID="itemPlaceHolder" 
                OnItemDataBound="idListView_ItemDataBound">

    <%--Plantilla de diseño de la Lista Html--%>
    <LayoutTemplate>
        <%--Control de servidor PlaceHolder, elemento que
            contendrá la plantilla <ItemTemplate />--%>
            <asp:PlaceHolder runat="server" id="itemPlaceHolder">

            </asp:PlaceHolder>
    </LayoutTemplate>

    <%--Plantilla de los elementos dinámicos de la Lista Html--%>
    <ItemTemplate>
        <div class="row">
            <div class="col-md-12">
                <div>
                    <strong runat="server" id="idFecha" ></strong>&nbsp;
                    <span runat="server" id="idNombre"></span>&nbsp;
                    <span runat="server" id="idEmail"></span>
                </div>
                <strong runat="server" id="idTitulo"></strong>
                <p runat="server" id="idComentario"></p>
                <hr />
            </div>
        </div>
    </ItemTemplate>
</asp:ListView>

<%--Control de servidor DataPager. Paginador de la Lista Html--%>
<asp:DataPager runat="server" ID="idDataPager" 
                PagedControlID="idListView" PageSize="5">
    <Fields>                        
        <asp:NextPreviousPagerField ButtonType="Button"
                                    ShowFirstPageButton="True" 
                                    ShowNextPageButton="False" 
                                    PreviousPageText="&#9668;" 
                                    FirstPageText="&#9668;&#9668;"
                                    ButtonCssClass="btn btn-sm btn-default" />
        <asp:NumericPagerField ButtonType="Button"
                                NumericButtonCssClass="btn btn-sm btn-default" 
                                CurrentPageLabelCssClass=""
                                NextPreviousButtonCssClass="btn btn-sm btn-default"/>
        <asp:NextPreviousPagerField ButtonType="Button"
                                    ShowLastPageButton="True" 
                                    ShowPreviousPageButton="False" 
                                    NextPageText="&#9658;" 
                                    LastPageText="&#9658;&#9658;"
                                    ButtonCssClass="btn btn-sm btn-default"/>
    </Fields>
</asp:DataPager> 

El Codebehind

A continuación, crearemos el código de Servidor que gestionará la página Blog.aspx en su archivo correspondiente Blog.aspx.cs (Codebehind):

    public partial class Blog : System.Web.UI.Page
    {
        private ApplicationDbContext _applicationDbContext;
        protected void Page_Load(object sender, EventArgs e)
        {
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            // SIMULAMOS UN DELAY PARA EL UpdateProgress
            System.Threading.Thread.Sleep(2000);

            // CARGA LOS DATOS EN EL ListView.
            using (_applicationDbContext = new ApplicationDbContext())
            {
                idListView.DataSource = _applicationDbContext.Posts.OrderByDescending(x => x.Fecha).ToList();
                idListView.DataBind();
            }            
        }

        protected void idSubmitBtn_Click(object sender, EventArgs e)
        {
            // SIMULAMOS UN DELAY PARA EL UpdateProgress
            System.Threading.Thread.Sleep(2000);

            // GUARDA EL OBJETO DEL FORM EN LA BD.
            using (_applicationDbContext = new ApplicationDbContext())
            {
                Post post = new Post()
                {
                    Nombre = idNombre.Text,
                    Email = idEmail.Text,
                    Titulo = idTitulo.Text,
                    Comentario = idComentario.Text
                };
                _applicationDbContext.Posts.Add(post);
                _applicationDbContext.SaveChanges();
            }

            // ESTABLECE EL PAGINADOR EN LA PRIMERA PÁGINA,
            // PARA PODER VER EL NUEVO REGISTRO INSERTADO.
            idDataPager.SetPageProperties(0, idDataPager.PageSize, true);

            // BORRA LOS DATOS DEL FORMULARIO
            idComentario.Text = string.Empty;
            idNombre.Text = string.Empty;
            idEmail.Text = string.Empty;
            idTitulo.Text = string.Empty;
        }

        protected void idListView_ItemDataBound(object sender, ListViewItemEventArgs e)
        {
            // ENLAZA LOS DATOS A CADA ELEMENTO DEL ListView.
            if (e.Item.ItemType == ListViewItemType.DataItem)
            {
                Post post = e.Item.DataItem as Post;
                ((HtmlGenericControl)e.Item.FindControl("idFecha")).InnerText = post.Fecha.ToShortDateString();
                ((HtmlGenericControl)e.Item.FindControl("idNombre")).InnerText = post.Nombre;
                ((HtmlGenericControl)e.Item.FindControl("idEmail")).InnerText = post.Email;
                ((HtmlGenericControl)e.Item.FindControl("idTitulo")).InnerText = post.Titulo;
                ((HtmlGenericControl)e.Item.FindControl("idComentario")).InnerText = post.Comentario;
            }
        }
    }

Nota: Como podemos ver en el código de Servidor, hemos hecho referencia a un objeto privado del tipo ApplicationDbContext (Contexto de datos de Entity Framework). Como no es el objetivo de este artículo explicar como enlazar Entity Framework a una base de datos, supondremos que ya hemos definido el DbContext de la aplicación con el correspondiente DbSet<Post> del modelo de datos Post.cs.

En este punto, y si todo ha ido bien, ya podemos ejecutar la aplicación desde la página Post.aspx y comenzar a crear registros (Posts) en la Base de Datos. Observaremos que cada vez que creamos un registro nuevo, la lista de Posts se actualiza en pantalla con todos los registros de la Base de Datos (incluido el nuevo elemento creado).

De momento, toda esta funcionalidad la realizamos enviando toda la página al Servidor (Postback), y recibiendo una nueva página actualizada con los resultados (de manera síncrona).

A continuación veremos cómo aplicar los cambios necesarios para transformar este proceso síncrono de envío y recepción de datos, en un proceso asíncrono (AJAX) donde solo enviaremos los datos del formulario al Servidor, y recibiremos una actualización parcial de la página con los nuevos resultados.

 

Aplicando la asincronía - AJAX

Lo interesante de transformar nuestro formulario Web síncrono  en un formulario AJAX totalmente funcional, es que no necesitaremos modificar nada del código ya desarrollado, ni escribir ninguna línea de código JavaScript adicional.

Todo el proceso de transformación hacia AJAX, lo realizaremos mediante los controles Web de ASP.NET WebForms <asp:ScriptManager /><asp:UpdatePanel />.

Una plantilla estándar de asincronía en ASP.NET WebForms - AJAX

El proceso a seguir para transformar un formulario Web estándar (Postback) en un formulario AJAX, lo podemos resumir con el siguiente ejemplo o plantilla de código:

<%--Control ScriptManager, Administra las bibliotecas de scripts--%>
<%--y los archivos de script AJAX de ASP.NET--%>
<asp:ScriptManager runat="server" ID="idScriptManager"> 
</asp:ScriptManager>
        
<%--Control UpdatePanel, habilita la funcionalidad AJAX asíncrona--%>
<asp:UpdatePanel runat="server" ID="idUpdatePanel">
            
    <%--Contenido que se actualizará de manera asíncrona--%>
    <ContentTemplate>

        <%--AQUÍ TODO EL CONTENIDO QUE SE ACTUALIZARÁ VÍA AJAX--%>

    </ContentTemplate>

    <%--Elemento/s Html que desencadenan la llamada AJAX--%>
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="idSubmitBtn" EventName="Click" />
    </Triggers>

</asp:UpdatePanel>

<%--Control UpdateProgress, AJAX Loader mientras se espera la respuesta asíncrona--%>
<asp:UpdateProgress runat="server" ID="idUpdateProgress" 
                    AssociatedUpdatePanelID="idUpdatePanel" DynamicLayout="true">
    <ProgressTemplate>
            
        <%--AQUÍ EL TEXTO O IMAGEN AJAX LOADER--%>

    </ProgressTemplate>
</asp:UpdateProgress>

Como vemos, todo se reduce a incluir dentro de la etiqueta <ContentTemplate>  <ContentTemplate /> del <asp:UpdatePanel /> el código de la página ASPX que queremos que se gestione y actualice vía AJAX.

El resultado final

Para finalizar, solo quedaría adaptar el código de la página Blog.aspx a la nueva estructura del control <asp:UpdatePanel />. Por supuesto, el Codebehind Blog.aspx.cs seguirá siendo el mismo, no hace falta modificarlo.

El código final sería el siguiente:

<%--Control ScriptManager, Administra las bibliotecas de scripts--%>
<%--y los archivos de script AJAX de ASP.NET--%>
<asp:ScriptManager runat="server" ID="sm"> 
</asp:ScriptManager>
            
<%--Control UpdatePanel, habilita la funcionalidad AJAX asíncrona--%>
<asp:UpdatePanel runat="server" ID="idUpdatePanel">
            
    <%--Contenido que se actualizará de manera asíncrona--%>
    <ContentTemplate>

        <%--AQUÍ TODO EL CONTENIDO QUE SE ACTUALIZARÁ VÍA AJAX--%>

        <%--FORMULARIO PARA ENVÍO DE DATOS--%>
        <h3>BlogPost</h3>

        <div class="row">
            <div class="col-md-6">
                <div class="form-group">
                    <asp:TextBox runat="server" ID="idNombre" CssClass="form-control input-sm" 
                        placeholder="Nombre"></asp:TextBox>
                </div>
            </div>
            <div class="col-md-6">
                <div class="form-group">
                    <asp:TextBox runat="server" ID="idEmail" CssClass="form-control input-sm" 
                        placeholder="Email" TextMode="Email" ></asp:TextBox>
                </div>
            </div>
            <div class="col-md-12">
                <div class="form-group">
                    <asp:TextBox runat="server" ID="idTitulo" CssClass="form-control input-sm" 
                        placeholder="Titulo"></asp:TextBox>
                </div>
            </div>
            <div class="col-md-12">
                <div class="form-group">
                    <asp:TextBox runat="server" ID="idComentario" CssClass="form-control input-sm" 
                        placeholder="Comentario" TextMode="MultiLine" Rows="5"></asp:TextBox>
                </div>
            </div>
        </div>

        <div class="row">
            <div class="col-md-6">
                <div class="form-group float-right">                            
                    <asp:Button runat="server" ID="idSubmitBtn" OnClick="idSubmitBtn_Click" 
                        CssClass="btn btn-primary" Text="Enviar Datos"/>                            
                </div>
            </div>
        </div>


        <%--LISTA DE POSTS ENVIADOS AL SERVIDOR --%>
        <h3>PostList</h3>

        <%--Control de servidor ListView--%>
        <asp:ListView runat="server" ID="idListView" 
                        ItemPlaceholderID="itemPlaceHolder" 
                        OnItemDataBound="idListView_ItemDataBound">

            <%--Plantilla de diseño de la Lista Html--%>
            <LayoutTemplate>
                <%--Control de servidor PlaceHolder, elemento que
                    contendrá la plantilla <ItemTemplate>--%>
                    <asp:PlaceHolder runat="server" id="itemPlaceHolder">
                    </asp:PlaceHolder>
            </LayoutTemplate>

            <%--Plantilla de los elementos dinámicos de la Lista Html--%>
            <ItemTemplate>
                <div class="row">
                    <div class="col-md-12">
                        <div>
                            <strong runat="server" id="idFecha" ></strong>&nbsp;
                            <span runat="server" id="idNombre"></span>&nbsp;
                            <span runat="server" id="idEmail"></span>
                        </div>
                        <strong runat="server" id="idTitulo"></strong>
                        <p runat="server" id="idComentario"></p>
                        <hr />
                    </div>
                </div>
            </ItemTemplate>
        </asp:ListView>

        <%--Control de servidor DataPager. Paginador de la Lista Html--%>
        <asp:DataPager runat="server" ID="idDataPager" 
                        PagedControlID="idListView" PageSize="5">
            <Fields>                        
                <asp:NextPreviousPagerField ButtonType="Button"
                                            ShowFirstPageButton="True" 
                                            ShowNextPageButton="False" 
                                            PreviousPageText="&#9668;" 
                                            FirstPageText="&#9668;&#9668;"
                                            ButtonCssClass="btn btn-sm btn-default" />
                <asp:NumericPagerField ButtonType="Button"
                                        NumericButtonCssClass="btn btn-sm btn-default" 
                                        CurrentPageLabelCssClass=""
                                        NextPreviousButtonCssClass="btn btn-sm btn-default"/>
                <asp:NextPreviousPagerField ButtonType="Button"
                                            ShowLastPageButton="True" 
                                            ShowPreviousPageButton="False" 
                                            NextPageText="&#9658;" 
                                            LastPageText="&#9658;&#9658;"
                                            ButtonCssClass="btn btn-sm btn-default"/>
            </Fields>
        </asp:DataPager> 

    </ContentTemplate>
            
    <%--Elemento/s Html que desencadenan la llamada asíncrona--%>
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="idSubmitBtn" EventName="Click" />
    </Triggers>
            
</asp:UpdatePanel>

<%--Control UpdateProgress, AJAX Loader mientras se espera la respuesta asíncrona--%>
<asp:UpdateProgress runat="server" ID="idUpdateProgress" 
                    AssociatedUpdatePanelID="idUpdatePanel" DynamicLayout="true">
    <ProgressTemplate>
        <div style="position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);">
            <img id="idAjaxLoader" alt="Enviando ..." src="Images/loading.gif" />
        </div>
    </ProgressTemplate>
</asp:UpdateProgress>

Como vemos, el código y las configuraciones adicionales son bastante simples y fáciles de entender.

Nota: Para cualquier duda o pregunta utilizar la zona de comentarios del artículo.

Ahora solo quedaría volver a ejecutar la aplicación desde la página Post.aspx, y comprobar que la funcionalidad sigue siendo la misma, pero esta vez utilizando AJAX.

Nota: Como podemos ver en el código, en el control  <asp:UpdateProgress /> hemos utilizado la imagen loading.gif a modo de AJAX Loader. Está imagen no es más que un Gif animado que simula un proceso de espera. Por si lo necesitan para el ejemplo, está disponible para descargar al final del artículo.

Descargas

loading.gif

   EtiquetasASP.NET Web Forms AJAX

  Compartir


  Nuevo comentario

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

  Comentarios

Rafael Acosta Administrador Rafael Acosta

@Pablo:

Te recomiendo que leas detenidamente el artículo de este Blog Entity Framework 6 y Sql Server Compact CE en ASP.NET MVC 5 , donde podrás ver cómo implementar una base de datos ligera, compacta, totalmente funcional e integrada en el propio proyecto, en conjunción con el ORM Entity Framework.



Recuerda que compartir los artículos del Blog en redes sociales y hacer click en los banners de publicidad, es fundamental para que este Blog siga ofreciendo publicaciones de calidad en español y respondiendo a todas las  preguntas que se plantean.  



Un saludo.


Pablo Pablo

Hola! , muy buen articulo! Me podrias decir como montar la BD, como hacer la conexion?
Muchas gracias de antemano!
Mauricio Mena Cousin Mauricio Mena Cousin

Hola mi nombre es Mauricio tengo que algunos errores bro, pero me gusto mucho tu ayuda, pero si me pudieras a mi correo te lo agradeciería mucho bro para corregir mis errores mauriciomenacousin@gmail.com


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