ASP.NET MVC e AJAX: exibindo o progresso de execução de uma View

Veja neste artigo como exibir em aplicações ASP.NET MVC uma imagem indicando que a execução da Action associada a uma View está em progresso.

Uma das grandes preocupações atuais dentro do desenvolvimento de soluções Web gira em torno da ideia de se garantir uma boa experiência do usuário. Este conceito refere-se à construção de soluções com elementos visuais sofisticados, os quais são implementados de uma maneira que simplifique a interação de usuários com as funcionalidades disponibilizadas por uma aplicação. A construção de sites em conformidade com essas características acontece por meio da utilização de um conjunto de recursos conhecidos como AJAX (sigla do inglês “Asynchronous JavaScript and XML”).

O uso de técnicas envolvendo AJAX é extremamente comum nas interfaces de redes sociais e de portais que disponibilizam serviços de webmail, sendo facilmente observável na implementação de características como:

No caso da exibição de informações indicando o progresso de uma ação, a presença deste tipo de comportamento impede se crie a falsa impressão de que a aplicação travou durante um processamento mais demorado. Em ASP.NET Web Forms isto é conseguido por meio da utilização do controle UpdateProgress, o qual faz parte de um conjunto de extensões AJAX para este tipo de solução. Já em projetos construídos sob o framework ASP.NET MVC, o comum é que se utilize dentro do código que define uma View os métodos disponibilizados pelo objeto Ajax (instância da classe AjaxHelper, esta última localizada no namespace System.Web.Mvc).

O objetivo deste artigo é demonstrar como o objeto Ajax pode ser utilizado para demonstrar o progresso de uma ação em Views do ASP.NET MVC. Para isto, será construído um projeto em que será possível a consulta a informações de produtos em um banco de dados SQL Server.

Criando a solução de exemplo

A aplicação apresentada neste artigo foi criada a partir do Visual Studio 2012 Professional, fazendo uso para isto do .NET Framework 4.5 e da versão 4.0 do ASP.NET MVC. O exemplo a ser implementado fará uso de um banco de dados SQL Server (Northwind) via ADO.NET, o qual servirá de base para a execução de consultas aos produtos cadastrados nesta base.

Para gerar o projeto de testes será necessário, dentro do Visual Studio, acessar o menu File, opção New e, por fim, a opção Project. Dentro da tela New Project (Figura 1) selecionar o template ASP.NET MVC 4 Web Application, preenchendo o campo Name com o nome da aplicação a ser gerada (“TesteUpdateProgressMVC”, neste caso); no campo Location é possível ainda definir o diretório no qual serão criados os arquivos para este projeto.


Figura 1: Criando um projeto ASP.NET MVC 4 para testes

Aparecerá então uma janela como a que consta na Figura 2. Deverão ser escolhidos o template para a criação do projeto (selecionar “Internet Application”), assim como o Engine utilizado para a geração das Views (marcar a opção “Razor”).


Figura 2: Selecionando o tipo de template

Configurando o acesso à base de dados

Para a obtenção das informações utilizadas pela aplicação de exemplo será empregado o banco de dados Northwind. Maiores informações sobre como habilitar esta base para uso no SQL Server 2012 podem ser encontradas no link:

http://msdn.microsoft.com/en-us/library/vstudio/8b6y4c7s.aspx

Com a base de dados já configurada, adicionar ao arquivo Web.config do projeto de testes a string de conexão “Northwind” (conforme indicado na Listagem 1).

Listagem 1: Ajustes no arquivo Web.config da aplicação

<?xml version="1.0" encoding="utf-8"?> <configuration> ... <connectionStrings> <add name="Northwind" connectionString="Data Source=.;Initial Catalog=Northwind;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings> ... </configuration>

Implementando as classes de acesso a dados

Com o projeto TesteUpdateProgressMVC já criado e o acesso à base de dados devidamente configurado, chega o momento de prosseguir com a construção dos tipos empregados na manipulação de informações sobre produtos. As estruturas descritas nesta seção pertencerão à camada Model (namespace TesteUpdateProgressMVC.Models).

Primeiramente será implementada a classe Produto (Listagem 2). Foram definidos neste tipo (sob a forma de propriedades) o código do produto, o nome do mesmo, bem como a identificação da empresa que fornece tal item.

Listagem 2: Classe Produto

using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace TesteUpdateProgressMVC.Models { public class Produto { public string CodProduto { get; set; } public string NomeProduto { get; set; } public string Fornecedor { get; set; } } }

Já na Listagem 3 está o código referente à classe ProdutoDAO. Este tipo baseia-se em um padrão conhecido Data Access Object (DAO), tendo por finalidade centralizar o acesso à base de dados para a manipulação de informações sobre produtos (por meio da operação ObterInformacoesProdutos).

Quanto ao funcionamento do método ObterInformacoesProdutos, é possível destacar:

Listagem 3: Classe ProdutoDAO

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Configuration; using System.Data; using System.Data.SqlClient; namespace TesteUpdateProgressMVC.Models { public class ProdutoDAO { public List<Produto> ObterInformacoesProdutos( string nomeProduto) { List<Produto> resultado = new List<Produto>(); using (SqlConnection conexao = new SqlConnection(ConfigurationManager .ConnectionStrings["Northwind"].ConnectionString)) { string valorPesquisa = "%" + (!String.IsNullOrWhiteSpace(nomeProduto) ? nomeProduto.Trim().ToUpper() : String.Empty) + "%"; SqlCommand cmd = conexao.CreateCommand(); cmd.CommandText = "SELECT P.ProductID AS CodProduto " + ",P.ProductName AS NomeProduto " + ",S.CompanyName AS Fornecedor " + "FROM dbo.Products P " + "INNER JOIN dbo.Suppliers S ON " + "S.SupplierID = P.SupplierID " + "WHERE UPPER(ProductName) LIKE @PRODUTO " + "ORDER BY P.ProductName"; cmd.Parameters.Add("@PRODUTO", SqlDbType.VarChar).Value = valorPesquisa; conexao.Open(); using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { resultado.Add(new Produto(){ CodProduto = reader["CodProduto"].ToString(), NomeProduto = reader["NomeProduto"].ToString(), Fornecedor = reader["Fornecedor"].ToString() }); } reader.Close(); } conexao.Close(); } return resultado; } } }

Implementando o Controller que processará informações de produtos

Com os tipos da camada Model já definidos, deve-se proceder agora com a implementação do Controller que retornará o resultado de consultas à tabela de produtos.

Um novo Controller pode ser criado por meio da janela Solution Explorer: para isto clicar com o botão direito do mouse sobre a pasta Controllers, selecionando em seguida no menu de atalho o item Add, opção Controller. Aparecerá então a janela Add Controller; preencher o campo Controller Name com o valor "ProdutoController" (Figura 3), enquanto em Template deverá estar marcada a opção “Empty MVC Controller”.


Figura 3: Criando um novo Controller

Na Listagem 4 está a implementação da classe ProdutoController. Foi definido neste Controller apenas a Action ObterInformacoesProdutos, a qual devolverá como resultado de sua execução informações relativas a produtos da base Northwind: isso será feito tomando por base uma string em que constará parte do nome dos itens procurados (se um valor não for repassado à Action, todos os produtos cadastrados serão retornados).

Conforme é possível observar, o retorno da operação ObterInformacoesProdutos será uma instância do tipo PartialViewResult, a qual é gerada por meio de uma chamada ao método básico PartialView (definido na classe básica Controller). Esta característica indica que o resultado de ObterInformacoesProdutos será uma Partial View, um tipo de estrutura cujo conteúdo pode ser combinado a outras Views para a geração de uma página HTML.

Listagem 4: Classe ProdutoController

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TesteUpdateProgressMVC.Models; namespace TesteUpdateProgressMVC.Controllers { public class ProdutoController : Controller { public PartialViewResult ObterInformacoesProdutos( string nomeProduto) { List<Produto> informacoesProdutos = new ProdutoDAO().ObterInformacoesProdutos( nomeProduto); return PartialView(informacoesProdutos); } } }

OBSERVAÇÃO: Por questões de simplificação, o código referente ao Controller HomeController foi omitido, já que no mesmo não existem instruções complexas que justifiquem uma discussão mais aprofundada (o arquivo correspondente pode ser obtido através do download da solução aqui implementada, acessando para isto o link em que consta o material para este artigo).

Implementação das Views da aplicação

Por fim, será preciso criar a Partial View para a exibição de informações sobre produtos, bem como proceder com ajustes na View que conterá tais estruturas (a qual está relacionada à Action Index de HomeController).

No caso da classe ProdutoController, a criação da View que conterá o resultado da execução da Action ObterInformacoesProdutos pode ser feita clicando-se com o botão direito do mouse sobre o método correspondente; no menu de atalho apresentado selecionar a opção Add View.

Com a janela “Add View” ativa, certificar-se de que o campo “View Engine” está preenchido com o valor “Razor (CSHTML)”. Marcar então a opção “Create a strongly typed-view”, selecionando em “Model class” o tipo Continente; já em “Scaffold template”, escolher o valor “List”, a fim de que seja gerada uma listagem/tabela com os diferentes registros relativos aos dados de produtos. Concluindo este processo, selecionar ainda a opção “Create as a partial view”. Na Figura 5 estão ilustrados todos esses ajustes.


Figura 4: Criação de uma Partial View

Ao se acionar o botão Add da janela Add View será criado o arquivo ObterInformacoesProdutos.cshtml (Listagem 5). Trata-se de uma View “fortemente tipada”, ou seja, através da propriedade Model desta estrutura se tem acesso a uma coleção de objetos de um tipo específico (no caso, a classe Produto).

Listagem 5: Partial View ObterInformacoesProdutos.cshtml

@model IEnumerable<TesteUpdateProgressMVC.Models.Produto> <table> <tr> <th > Cód. Produto </th> <th > Descrição </th> <th > Fornecedor </th> </tr> @foreach (var item in Model) { <tr> <td > @item.CodProduto </td> <td> @item.NomeProduto </td> <td> @item.Fornecedor </td> </tr> } </table>

Antes de prosseguir com a implementação da View Index (a qual está associada a HomeController), será necessário incluir um arquivo GIF de nome “carregando.gif” na pasta Content do projeto TesteUpdateProgressMVC.

Esta imagem corresponde a um tipo de animação muito comum em sites Web, sendo normalmente empregada para indicar que uma ação está em progresso. O arquivo utilizado na aplicação de exemplo aqui apresentada foi gerado através do seguinte site:

http://www.ajaxload.info/

Ajustes precisarão ser realizados agora na View Index (Listagem 6), que está vinculada à Action de mesmo nome na classe HomeController (este Controller é gerado por default ao se utilizar o template “Internet Application”). Importante ressaltar que essa estrutura será exibida como página inicial ao se executar a aplicação por meio do Visual Studio.

Ao ser visualizada, a ideia é que a View Index exiba um campo para a pesquisa de produtos por nome. Se o usuário acionar então um botão para disparar a execução da consulta, será exibida a imagem indicando que a ação está em progresso e, finalmente, apresentados os dados referentes ao filtro informado.

Listagem 6: View Index.cshtml

@Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/jqueryval") <style type="text/css"> .progresso { display: none; position: fixed; top: 360px; left: 50%; margin-top: -50px; margin-left: -100px; vertical-align: middle; text-align: center; font-size: 16px; font-weight: bold; background-color: white; border: 1px solid black; height: 90px; width: 120px; } </style> @section featured { <section class="featured"> <div class="content-wrapper"> <hgroup class="title"> <h1>Utilizando o objeto Ajax (classe AjaxHelper)</h1> </hgroup> </div> </section> } <h2>Consulta ao Cadastro de Produtos</h2> <br /> Digite o nome do produto (ou parte desta identificação): @using (Ajax.BeginForm( "ObterInformacoesProdutos", "Produto", new AjaxOptions { UpdateTargetId = "divResultadoConsulta", LoadingElementId = "divCarregando" } )) { @Html.TextBox("nomeProduto") <input type="submit" value="Pesquisar" /> } <div id="divResultadoConsulta"> </div> <div id="divCarregando" class="progresso"> <img src="@Url.Content("~/Content/carregando.gif")" /> <br /> Carregando... </div>

Analisando o código do arquivo Index.cshtml, é possível observar:

Quanto à utilização do método BeginForm acionado por meio do objeto Ajax, foram preenchidos os seguintes parâmetros para esta operação:

Além da operação BeginForm, o objeto Ajax disponibiliza outras funcionalidades como o método ActionLink. Este último permite a geração de um link que aponta para uma Action, fazendo uso para isto dos mesmos recursos de AJAX já descritos para o método BeginForm.

Executando a aplicação de testes

Iniciando a execução do site de testes, será exibida uma tela como a que consta na Figura 5.


Figura 5: A aplicação TesteUpdateProgressMVC em execução

Preenchendo o campo de pesquisa com o valor “chef” e acionando o botão “Pesquisar”, aparecerá a indicação de que uma consulta à base está em progresso (Figura 6).


Figura 6: Executando uma consulta à base de produtos

Por fim, a Figura 7 demonstra o resultado após a conclusão de uma consulta à base de dados.


Figura 7: Resultado de uma consulta à base Northwind

Conclusão

Procurei com este artigo demonstrar, através de um exemplo simples, como o mesmo comportamento oferecido pelo componente UpdateProgress em Web Forms pode ser implementado numa aplicação ASP.NET MVC. A exibição de notificações indicando que uma ação está em progresso é, sem sombra de dúvidas, um recurso extremamente útil. Funcionalidades deste tipo ajudam a evitar que usuários abandonem um site, muitas vezes por acreditar que o mesmo travou durante o processamento de uma solicitação.

Espero que o conteúdo aqui apresentado possa ser útil em algum momento. Até uma próxima oportunidade!

Leia também

Artigos relacionados