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:

  • A indicação do progresso de uma ação durante o processamento da mesma;
  • O update/atualização de apenas partes de uma página, dispensando assim a necessidade de atualização de todo o conteúdo existente. Este tipo de prática tem como grande benefício tornar mais rápida e eficiente a interação de um usuário com as funcionalidades oferecidas por um site;
  • Suporte à comunicação assíncrona com Web Services, incluindo nisto a manipulação de informações nos formatos JSON e XML;
  • A execução de ações em períodos regulares de tempo, empregando para isso estruturas conhecidas como "timers".

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.

Criando um projeto ASP.NET MVC 4 para testes

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”).

Selecionando o tipo de template

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:

  • A partir da variável “conexao” (instância do tipo System.Data.SqlClient.SqlConnection) é estabelecida uma conexão com a base de dados utilizada pela aplicação de exemplo (empregando para isto a string de conexão “Northwind”);
  • A referência de nome “cmd” (objeto do tipo System.Data.SqlClient.SqlCommand) representa a instrução SQL que fará a consulta à tabela de produtos. Está sendo adicionada à propriedade Parameters um novo parâmetro, que conterá o nome de produto (ou parte deste) a ser pesquisado;
  • Ao se invocar o método ExecuteReader do objeto associado à variável “cmd” uma nova instância da classe SqlDataReader (namespace System.Data.SqlClient) é gerada. Por meio desta referência os diferentes registros serão lidos, acontecendo ainda a conversão de tais informações em instâncias do tipo Produto;
  • Os objetos da classe Produto são armazenados em uma coleção, a qual será devolvida como resultado da execução do método ObterInformacoesProdutos.

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”.

Criando um novo 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.

Criação de uma Partial View

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 style="text-align: center;  width: 150px;">
            Cód. Produto
        </th>
        <th style="width: 300px;">
            Descrição
        </th>
        <th style="width: 300px;">
            Fornecedor
        </th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td style="text-align: center;">
            @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:

  • Chamadas ao método Render da classe Scripts (namespace System.Web.Optimization). O objeto destas instruções é incluir as bibliotecas JQuery que permitem a utilização de AJAX em Views do ASP.NET MVC;
  • Uma classe CSS de nome “progresso”, na qual estão as definições para a aparência de um elemento div responsável por indicar que uma consulta está em progresso (por default o atributo display foi configurado com o valor “none”, a fim de que esta div seja exibida apenas durante ações envolvendo a utilização de AJAX);
  • O uso do método BeginForm, a partir do objeto Ajax. Este procedimento permitirá a geração de um formulário em que constará um campo chamado “nomeProduto” (seguindo o mesmo nome do parâmetro esperado pela Partial View ObterInformacoesProdutos em ProdutoController), além de um botão para disparar a execução de uma nova consulta à base de dados;
  • Uma div de nome “divResultadoConsulta”, a qual conterá o resultado de uma consulta efetuada junto ao banco Northwind;
  • Uma div chamada “divCarregando” e que faz uso da classe CSS "progresso" mencionada anteriormente. Este elemento HTML contém o arquivo carregando.gif, o qual será exibido enquanto uma consulta à base de dados estiver em curso.

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

  • O nome da Partial View (“ObterInformacoesProdutos”) cujo conteúdo HTML será exibido dentro da View Index.cshtml;
  • O Controller (“Produto”) em que esta Partial View foi definida;
  • Uma instância do tipo AjaxOptions (namespace System.Web.Mvc.Ajax) em que estão indicadas a div (“divResultadoConsulta”) aonde constará o resultado da Partial View ObterInformacoesProdutos (propriedade UpdateTargetId), assim como a div (“divCarregando”) utilizada na exibição da notificação de que uma consulta ao banco de dados está em progresso (este elemento HTML é referenciado na propriedade LoadingElementId).

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.

A aplicação TesteUpdateProgressMVC em execução

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).

Executando uma consulta à base de produtos

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.

Resultado de uma consulta à base Northwind

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