7. Implementação de Ajax em um aplicativo ASP.NET MVC
7.1. O papel do AJAX em um aplicativo web
Por enquanto, os exemplos de aprendizado estudados apresentam a seguinte arquitetura:
![]() |
Para passar de uma visualização [Vue1] para uma visualização [Vue2], o navegador:
- envia uma solicitação para a aplicação web;
- recebe a visualização [Vue2] e a exibe no lugar da visualização [Vue1].
Esse é o esquema clássico:
- solicitação do navegador;
- geração de uma visualização pelo servidor web em resposta ao cliente;
- exibição dessa nova visualização pelo navegador.
Existe outro modo de interação entre o navegador e o servidor web: AJAX (Asynchronous JavaScript and XML). Trata-se, na verdade, de interações entre a visualização exibida pelo navegador e o servidor web. O navegador continua fazendo o que sabe fazer, ou seja, exibir uma visualização HTML, mas agora é controlado por JavaScript incorporado na visualização HTML exibida. O esquema é o seguinte:
![]() |
- em [1], ocorre um evento na página exibida no navegador (clique em um botão, alteração de um texto, etc.). Esse evento é interceptado pelo JavaScript (JS) incorporado à página;
- em [2], o código JavaScript faz uma solicitação HTTP, da mesma forma que o navegador teria feito. A solicitação é assíncrona: o usuário pode continuar interagindo com a página sem ficar bloqueado pela espera pela resposta à solicitação HTTP. A solicitação segue o processo clássico de processamento. Nada (ou quase nada) a distingue de uma solicitação clássica;
- em [3], uma resposta é enviada ao cliente JS. Em vez de uma visualização HTML completa, é enviada uma visualização HTML parcial, um fluxo XML ou JSON (Notação de Objeto JavaScript) que é enviado;
- em [4], o JavaScript recupera essa resposta e a utiliza para atualizar uma região da página HTML exibida.
Para o usuário, há uma mudança na visualização, pois o que ele vê mudou. No entanto, não há recarregamento total da página, mas simplesmente uma modificação parcial da página exibida. Isso contribui para dar fluidez e interatividade à página: como não há recarregamento total da página, é possível gerenciar eventos que antes não eram gerenciados. Por exemplo, oferecer ao usuário uma lista de opções à medida que ele digita caracteres em um campo de entrada. A cada novo caractere digitado, uma solicitação AJAX é enviada ao servidor, que então retorna outras sugestões. Sem o Ajax, esse tipo de auxílio à digitação era impossível anteriormente. Não era possível recarregar uma nova página a cada caractere digitado.
7.2. Noções básicas de JQuery e JavaScript
Frequentemente, incorporamos a biblioteca JavaScript JQuery às nossas páginas. Nela, encontramos a linha:
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
Observação: adapte a versão do jQuery à versão do seu Visual Studio.
A tecnologia Ajax do ASP.NET MVC utiliza o JQuery. Nós mesmos escreveremos alguns scripts JQuery. Por isso, apresentaremos agora os conceitos básicos de JQuery que você precisa conhecer para entender os scripts deste capítulo.
Criamos um novo projeto [Exemple-04] dentro de nossa solução [Exemples]:
![]() |
Para utilizar o Ajax com o ASP.NET e o MVC, é necessário incluir uma linha no arquivo de configuração [Web.config] e [1]:
<appSettings>
...
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>
A linha 3 permite o uso do Ajax nas visualizações ASP.NET. Ela está presente por padrão.
Criamos um arquivo HTML [JQuery-01.html] na pasta [Content] do novo projeto [2]:
![]() |
Esse arquivo terá o seguinte conteúdo:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JQuery-01</title>
<script type="text/javascript" src="/Scripts/jquery-1.8.2.min.js"></script>
</head>
<body>
<h3>Rudiments de JQuery</h3>
<div id="element1">
Elément 1
</div>
</body>
</html>
- linha 6: importação de JQuery (adapte a versão à sua versão do Visual Studio);
- linhas 10-12: um elemento da página com o ID [element1]. Vamos trabalhar com esse elemento.
Visualizamos esses arquivos no navegador Google Chrome: [4] e [5]:
![]() |
No Google Chrome, digite [Ctrl-Maj-I] para exibir as ferramentas de desenvolvimento [6]. A aba [Console] [7] permite executar código JavaScript. A seguir, apresentamos comandos JavaScript a serem digitados, acompanhados de uma explicação.
JS | resultado |
|
: retorna a coleção de todos os elementos com o id [element1]; portanto, normalmente uma coleção de 0 ou 1 elemento, pois não é possível ter dois ids idênticos em uma página HTML. | ![]() |
|
: aplica o texto [blabla] a todos os elementos da coleção. Isso faz com que o conteúdo exibido pela página seja alterado | ![]() |
|
oculta os elementos da coleção. O texto [blabla] não é mais exibido. | ![]() |
|
: exibe a coleção novamente. Isso nos permite ver que o elemento com o id [element1] possui o atributo CSS style='display: none;', o que faz com que o elemento fique oculto. | |
|
: exibe os elementos da coleção. O texto [blabla] aparece novamente. É o atributo CSS style='display: block;' que garante essa exibição. | ![]() |
|
: define um atributo para todos os elementos da coleção. O atributo aqui é [style] e seu valor é [color: red]. O texto [blabla] fica em vermelho. | ![]() |
![]() | |
![]() |
Observe-se que o URL do navegador não sofreu alterações durante todas essas operações. Não houve troca de dados com o servidor web. Tudo ocorre internamente no navegador. Agora, vamos visualizar o código-fonte da página:
![]() |
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>JQuery-01</title>
<script type="text/javascript" src="/Scripts/jquery-1.8.2.min.js"></script>
</head>
<body>
<h3>Rudiments de JQuery</h3>
<div id="element1">
Elément 1
</div>
</body>
</html>
Este é o texto inicial. Ele não reflete de forma alguma as alterações que fizemos no elemento nas linhas 10 a 12. É importante lembrar disso ao depurar JavaScript. Portanto, muitas vezes é inútil visualizar o código-fonte da página exibida. Para conhecer o código-fonte da página atualmente exibida, proceda da seguinte maneira:
![]() |
Já sabemos o suficiente para compreender os scripts JS que virão a seguir.
7.3. Atualização de uma página com um fluxo HTML
7.3.1. As visualizações
Propomos estudar a seguinte aplicação:
![]() |
- em [1], a hora de carregamento da página;
- em [2], realizam-se as quatro operações aritméticas com dois números reais A e B;
- em [3], a resposta do servidor é exibida em uma área da página;
- em [4], a hora do cálculo. Essa hora é diferente da hora de carregamento da página [5]. Esta última é igual a [1], o que mostra que a região [6] não foi recarregada. Além disso, o URL e o [7] da página não se alteraram.
7.3.2. O controlador, as ações, o modelo, a visualização
Criamos um controlador chamado [Premier]:
![]() |
Para exibir a vista inicial, criamos a seguinte ação [Action01Get]:
[HttpGet]
public ViewResult Action01Get()
{
ViewModel01 modèle = new ViewModel01();
modèle.HeureChargement = DateTime.Now.ToString("hh:mm:ss");
return View(modèle);
}
- linha 4: instanciação do modelo da vista;
- linha 5: inicialização do tempo de carregamento da visualização;
- linha 6: exibição da vista [Action10Get.cshtml] e de seu modelo.
O modelo [ViewModel01] é o seguinte:
![]() |
using System;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_04.Models
{
[Bind(Exclude = "AplusB, AmoinsB, AmultipliéparB, AdiviséparB, Erreur, HeureChargement, HeureCalcul")]
public class ViewModel01
{
// formulário
[Required(ErrorMessage="Donnée requise")]
[Display(Name="Valeur de A")]
[Range(0, Double.MaxValue, ErrorMessage = "Tapez un nombre positif ou nul")]
public double A { get; set; }
[Required(ErrorMessage = "Donnée requise")]
[Display(Name = "Valeur de B")]
[Range(0, Double.MaxValue, ErrorMessage="Tapez un nombre positif ou nul")]
public double B { get; set; }
// resultados
public string AplusB { get; set; }
public string AmoinsB { get; set; }
public string AmultipliéparB { get; set; }
public string AdiviséparB { get; set; }
public string Erreur { get; set; }
public string HeureChargement { get; set; }
public string HeureCalcul { get; set; }
}
}
- linhas 11-14: o valor A do formulário;
- linhas 15-18: o valor B do formulário;
- linhas 21-24: os resultados das quatro operações aritméticas em A e B;
- linha 25: o texto de um possível erro;
- linha 26: a hora em que a visualização foi carregada no navegador;
- linha 27: a hora do cálculo dos campos das linhas 21-24;
- linha 7: este modelo de visualização também é um modelo de ação. Excluem-se deste último os campos que não são enviados pelo navegador.
A visualização [Action01Get.cshtml] é a seguinte:
![]() |
@model Exemple_04.Models.ViewModel01
@{
Layout = null;
AjaxOptions ajaxOpts = new AjaxOptions
{
UpdateTargetId = "résultats",
HttpMethod = "post",
Url = Url.Action("Action01Post"),
LoadingElementId = "loading",
LoadingElementDuration = 1000
};
}
<!DOCTYPE html>
<html lang="fr-FR">
<head>
<meta name="viewport" content="width=device-width" />
<title>Ajax-01</title>
<link rel="stylesheet" href="~/Content/Site.css" />
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/globalize.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.en-US.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script type="text/javascript" src="~/Scripts/myScripts-01.js"></script>
</head>
<body>
<h2>Ajax - 01</h2>
<p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
<h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
@using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
{
<table>
<thead>
<tr>
<th>@Html.LabelFor(m => m.A)</th>
<th>@Html.LabelFor(m => m.B)</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Html.TextBoxFor(m => m.A)</td>
<td>@Html.TextBoxFor(m => m.B)</td>
</tr>
<tr>
<td>@Html.ValidationMessageFor(m => m.A)</td>
<td>@Html.ValidationMessageFor(m => m.B)</td>
</tr>
</tbody>
</table>
<p>
<input type="submit" value="Calculer" />
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
<a href="javascript:postForm()">Calculer</a>
</p>
}
<hr />
<div id="résultats" />
</body>
</html>
- linha 1: a vista tem como modelo um tipo [ViewModel01];
- linha 21: é necessário o JQuery tanto para as validações quanto para o Ajax;
- linhas 22-23: as bibliotecas de validação;
- linhas 24-26: as bibliotecas de internacionalização;
- linha 27: a biblioteca Ajax;
- linha 28: uma biblioteca JavaScript local;
- linha 33: exibição da hora de carregamento da visualização;
- linha 35: um formulário Ajax — voltaremos a isso mais tarde;
- linhas 40-41: rótulos para a inserção dos números A e B;
- linhas 46-47: campos de entrada dos números A e B;
- linhas 50-51: mensagens de erro para as entradas dos números A e B;
- linha 56: o botão que envia o formulário. Ele será enviado por meio de uma solicitação Ajax;
- linha 57: uma imagem de espera exibida durante a solicitação Ajax;
- linha 58: um link para enviar o formulário por meio de uma solicitação Ajax;
- linha 62: uma tag <div> com o id [résultats]. É aqui que colocaremos o fluxo HTML enviado pelo servidor web.
Esta visualização exibe a seguinte página:
![]() |
Vamos agora examinar o código que implementa o Ajax no formulário:
...
@{
Layout = null;
AjaxOptions ajaxOpts = new AjaxOptions
{
UpdateTargetId = "résultats",
HttpMethod = "post",
Url = Url.Action("Action01Post"),
LoadingElementId = "loading",
LoadingElementDuration = 1000
};
}
...
@using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
{
....
<p>
<input type="submit" value="Calculer" />
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
<a href="javascript:postForm()">Calculer</a>
</p>
}
...
<div id="résultats" />
- linha 15: em vez de usar [@Html.BeginForm], usamos [@Ajax.BeginForm]. Esse método admite várias sobrecargas. A utilizada tem a seguinte assinatura:
Ajax.BeginForm(string ActionName, RouteValueDictionary routeValues, AjaxOptions ajaxOptions, IDictionary<string,object> htmlAttributes)
Utilizamos aqui os seguintes parâmetros efetivos:
Action01Post: o nome da ação que processará o POST do formulário,
null: não há informações de rota a serem fornecidas,
ajaxOpts: as opções da chamada Ajax. Elas foram definidas nas linhas 6 a 10,
new { id = "formulário" }: para atribuir o atributo [id='formulaire'] à tag <form> gerada;
As opções Ajax utilizadas são as seguintes:
- linha 8: o URL destino da solicitação Ajax HTTP;
- linha 7: método da solicitação Ajax HTTP;
- linha 6: id da região da página que será atualizada pela resposta à solicitação Ajax;
- linha 9: id da região da página que será exibida durante a solicitação Ajax – geralmente uma imagem de espera. Aqui, será exibida a linha 20. Ela contém uma imagem animada que simboliza uma espera. Inicialmente, essa imagem fica oculta pelo estilo [display : none];
- linha 10: tempo de espera em milissegundos antes que a imagem animada seja exibida; neste caso, 1 segundo.
O código HTML gerado pelo formulário Ajax é o seguinte:
<form action="/Premier/Action01Post" data-ajax="true" data-ajax-loading="#loading" data-ajax-loading-duration="1000" data-ajax-method="post" data-ajax-mode="replace" data-ajax-update="#résultats" data-ajax-url="/Premier/Action01Post" id="formulaire" method="post"> <table>
...
<p>
<input type="submit" value="Calculer" />
<img id="loading" style="display: none" src="/Content/images/indicator.gif" />
<a href="javascript:postForm()">Calculer</a>
</p>
</form>
<hr />
<div id="résultats" />
- linha 1: a tag <form> gerada. Observe os atributos [data-ajax-attr], que refletem os valores dos campos do objeto do tipo [AjaxOptions] associado à solicitação Ajax. Esses atributos são gerenciados pela biblioteca Ajax. Sem eles, a tag <form> passa a ser:
<form action="/Premier/Action01Post" id="formulaire" method="post">
...
<p>
<input type="submit" value="Calculer" />
<img id="loading" style="display: none" src="/Content/images/indicator.gif" />
<a href="javascript:postForm()">Calculer</a>
</p>
</form>
Estamos, então, diante de um formulário HTML clássico. É esse código que será executado se o usuário desativar o JavaScript em seu navegador. As linhas 5 e 6 ficam, então, sem uso.
7.3.3. A ação [Action01Post]
A ação [Action01Post], que processa a solicitação Ajax HTTP, é a seguinte:
[HttpPost]
public PartialViewResult Action01Post(FormCollection postedData, SessionModel session)
{
// simulação de espera
Thread.Sleep(2000);
// instanciação do modelo da ação
ViewModel01 modèle = new ViewModel01();
// hora do cálculo
modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
// atualização do modelo
TryUpdateModel(modèle, postedData);
if (!ModelState.IsValid)
{
// é retornado um erro
modèle.Erreur = getErrorMessagesFor(ModelState);
return PartialView("Action01Error", modèle);
}
// a cada duas vezes, simula-se um erro
int val = session.Randomizer.Next(2);
if (val == 0)
{
modèle.Erreur = "[erreur aléatoire]";
return PartialView("Action01Error", modèle);
}
// cálculos
modèle.AplusB = string.Format("{0}", modèle.A + modèle.B);
modèle.AmoinsB = string.Format("{0}", modèle.A - modèle.B);
modèle.AmultipliéparB = string.Format("{0}", modèle.A * modèle.B);
modèle.AdiviséparB = string.Format("{0}", modèle.A / modèle.B);
// visualização
return PartialView("Action01Success", modèle);
}
- linha 1: a ação processa apenas um [POST];
- linha 2: ela aceita como modelo de ação:
- [FormCollection postedData]: o conjunto de valores enviados pela solicitação Ajax POST,
- [SessionModel session]: os elementos da sessão. Utiliza-se aqui uma técnica descrita no parágrafo 4.10;
- linha 2: a ação retornará um fragmento HTML e não uma página HTML completa;
- linha 5: artificialmente, fazemos uma pausa de dois segundos para simular uma ação Ajax demorada;
- linha 7: um modelo do tipo [ViewModel01] é instanciado;
- linha 9: o tempo de cálculo é inicializado;
- linha 11: tenta-se atualizar o modelo do tipo [ViewModel01] com os valores enviados. Vale lembrar que são dois: os valores dos números A e B;
- linha 12: verifica-se se essa atualização foi bem-sucedida ou não;
- linha 15: em caso de erro, preenche-se o campo [Erreur] do modelo;
- linha 16: retorna-se uma visualização parcial [Action01Error.cshtml] utilizando o modelo [ViewModel01];
- linhas 19-24: a cada duas vezes, simula-se um erro;
- linha 19: gera-se um número inteiro aleatório no intervalo [0,1]. O gerador de números é obtido na sessão;
- linha 20: se o valor gerado for 0, simula-se um erro;
- linha 22: a mensagem de erro é inserida no modelo;
- linha 23: retorna-se uma visualização parcial [Action01Error.cshtml] utilizando o modelo [ViewModel01];
- linhas 26-29: os cálculos aritméticos sobre os números A e B são realizados e os resultados inseridos no modelo na forma de cadeias de caracteres;
- linha 31: é gerada uma visualização parcial [Action01Success.cshtml] utilizando o modelo [ViewModel01];
7.3.4. A visualização [Action01Error]
A visualização [Action01Error.cshtml] é a seguinte:
@model Exemple_04.Models.ViewModel01
<h4>Résultats</h4>
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p style="color: red;">Une erreur s'est produite : @Model.Erreur</p>
Vale lembrar que esse fluxo parcial HTML será enviado em resposta à solicitação Ajax HTTP do tipo POST e será inserido na página na região com o ID [résultats]. Todas essas informações provêm da configuração Ajax utilizada na página principal [Action01Get.cshtml]:
@model Exemple_04.Models.ViewModel01
@{
Layout = null;
AjaxOptions ajaxOpts = new AjaxOptions
{
UpdateTargetId = "résultats",
HttpMethod = "post",
Url = Url.Action("Action01Post"),
LoadingElementId = "loading",
LoadingElementDuration = 1000
};
}
Aqui está um exemplo de resposta com erro:
![]() |
7.3.5. A visualização [Action01Success]
A visualização [Action01Success.cshtml] é a seguinte:
@model Exemple_04.Models.ViewModel01
<h4>Résultats</h4>
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A+B=@Model.AplusB</p>
<p>A-B=@Model.AmoinsB</p>
<p>A*B=@Model.AmultipliéparB</p>
<p>A/B=@Model.AdiviséparB</p>
Mais uma vez, esse fluxo parcial HTML será enviado em resposta à solicitação Ajax HTTP do tipo POST e inserido na página na região com o ID [résultats]:
![]() |
7.3.6. G estão da sessão
Vimos que o [Action01Post] utilizava a sessão. O modelo da sessão é do tipo [SessionModel], conforme segue:
using System;
namespace Exemple_03.Models
{
public class SessionModel
{
public Random Randomizer { get; set; }
}
}
A sessão é inicializada em [Global.asax]:
// Sessão
protected void Session_Start()
{
SessionModel sessionModel=new SessionModel();
sessionModel.Randomizer=new Random(DateTime.Now.Millisecond);
Session["data"] = sessionModel;
}
A associação da sessão a um modelo é feita no [Application_Start]:
protected void Application_Start()
{
...
// ligadores de modelo
ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
}
A classe [SessionModelBinder] foi descrita.
7.3.7. Gerenciamento da imagem de espera
@model Exemple_04.Models.ViewModel01
@{
Layout = null;
AjaxOptions ajaxOpts = new AjaxOptions
{
...
LoadingElementId = "loading",
LoadingElementDuration = 1000
};
}
...
<body>
...
@using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
{
...
<p>
<input type="submit" value="Calculer" />
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
<a href="javascript:postForm()">Calculer</a>
</p>
}
...
Quando a solicitação Ajax é iniciada, a região com o ID [loading], linha 7, é exibida após um segundo ([ligne 8]). Essa região corresponde à imagem da linha 21, inicialmente oculta. Isso resulta na seguinte interface:
![]() |
7.3.8. Gerenciamento do link [Calculer]
Vamos examinar o link [Calculer] da página principal [Action01Get.cshtml]:
<head>
<meta name="viewport" content="width=device-width" />
<title>Ajax-01</title>
...
<script type="text/javascript" src="~/Scripts/myScripts-01.js"></script>
</head>
<body>
<h2>Ajax - 01</h2>
<p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
<h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
@using (Ajax.BeginForm("Action01Post", null, ajaxOpts, new { id = "formulaire" }))
{
...
<p>
<input type="submit" value="Calculer" />
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
<a href="javascript:postForm()">Calculer</a>
</p>
}
<hr />
<div id="résultats" />
- linha 18: um clique no link [Calculer] faz com que a função JS [postForm] seja executada. Essa função está definida no arquivo [myScripts-01.js], na linha 5. O script é o seguinte:
![]() |
function postForm() {
// faz-se uma chamada Ajax manualmente com JQuery
var loading = $("#loading");
var formulaire = $("#formulaire");
var résultats = $('#resultados');
$.ajax({
url: '/Premier/Action01Post',
type: 'POST',
data: formulaire.serialize(),
dataType: 'html',
begin: loading.show(),
success: function (data) {
loading.hide()
résultats.html(data);
}
})
}
// http://blog.instance-factory.com/?p=268
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseDate(value));
}
jQuery.extend(jQuery.validator.methods, {
range: function (value, element, param) {
//Use o plug-in de globalização para analisar o valor
var val = Globalize.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
As funções das linhas 19 a 37 já foram abordadas no parágrafo 6.1. Elas gerenciam a internacionalização das páginas. Não voltaremos a abordá-las. Nas linhas 1 a 17, fazemos manualmente a chamada Ajax que, no caso do botão [Calculer], era feita pela biblioteca Ajax associada ao projeto. Para isso, utilizamos a biblioteca JQuery associada ao projeto.
- linha 3: uma referência ao componente com id [loading]. [$("#loading")] retorna a coleção de elementos com id [loading]. Há apenas um;
- linha 4: uma referência ao componente com o ID [formulaire];
- linha 5: uma referência ao componente com o ID [résultats];
- linha 6: a chamada Ajax com suas opções;
- linha 7: o URL, destino da chamada Ajax;
- linha 8: o método HTTP utilizado;
- linha 9: os dados enviados. [formulaire.serialize] cria a string [A=val1&B=val2] a partir do campo POST do formulário com o ID [formulaire];
- linha 10: o tipo de dados esperado como retorno. Sabe-se que o servidor retornará um fluxo HTML;
- linha 11: o método a ser executado quando a solicitação for iniciada. Aqui, indica-se que é necessário exibir o componente com o ID [loading]. Trata-se da imagem animada de espera;
- linha 12: o método a ser executado caso a solicitação Ajax seja bem-sucedida. O parâmetro [data] é a resposta completa do servidor. Sabemos que se trata de um fluxo HTML;
- linha 13: ocultamos o sinal de espera;
- linha 14: atualiza-se o componente com o ID [résultats] com o valor HTML do parâmetro [data].
Sugere-se ao leitor que teste o link [Calculer]. Ele funciona como o botão [Calculer], com exceção de uma anomalia. Depois de usar esse link, é possível enviar valores inválidos para A e B:
![]() |
- nos links [1] e [2], foram inseridos valores inválidos. Eles são sinalizados pelos validadores do lado do cliente;
- em [3], clicou-se no link [Calculer];
- no [4], houve um [POST], já que obtém-se a resposta [4].
Quando os valores são inválidos e clicamos no botão [Calculer], o [POST] para o servidor não ocorre. No mesmo caso, com o link [Calculer], o [POST] para o servidor é enviado. Portanto, há um comportamento do botão [Calculer] que não conseguimos reproduzir com o link [Calculer]. Em vez de tentar resolver esse problema agora, vamos deixá-lo para um exemplo posterior, que também ilustrará outro problema de validação do lado do cliente.
7.4. Atualização de uma página HTML com um fluxo JSON
No exemplo anterior, o servidor web respondia à solicitação Ajax HTTP com um fluxo HTML. Nesse fluxo, havia dados acompanhados pela formatação HTML. Propomos retomar o exemplo anterior, mas desta vez com respostas JSON (JavaScript Object Notation) contendo apenas os dados. A vantagem é que, dessa forma, são transmitidos menos bytes.
7.4.1. A ação [Action02Get]
A ação [Action02Get] será o ponto de entrada do novo aplicativo. Seu código é o seguinte:
@model Exemple_04.Models.ViewModel02
@{
Layout = null;
AjaxOptions ajaxOpts = new AjaxOptions
{
HttpMethod = "post",
Url = Url.Action("Action02Post"),
LoadingElementId = "loading",
LoadingElementDuration = 1000,
OnBegin = "OnBegin",
OnFailure = "OnFailure",
OnSuccess = "OnSuccess",
OnComplete = "OnComplete"
};
}
<!DOCTYPE html>
<html lang="fr-FR">
<head>
<meta name="viewport" content="width=device-width" />
<title>Ajax-02</title>
....
<script type="text/javascript" src="~/Scripts/myScripts-02.js"></script>
</head>
<body>
<h2>Ajax - 02</h2>
<p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
<h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
@using (Ajax.BeginForm("Action02Post", null, ajaxOpts, new { id = "formulaire" }))
{
...
<p>
<input type="submit" value="Calculer" />
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
<a href="javascript:postForm()">Calculer</a>
</p>
}
<hr />
<div id="entete">
<h4>Résultats</h4>
<p><strong>Heure de calcul : <span id="heureCalcul"/></strong></p>
</div>
<div id="résultats">
<p>A+B=<span id="AplusB"/></p>
<p>A-B=<span id="AmoinsB"/></p>
<p>A*B=<span id="AmultipliéparB"/></p>
<p>A/B=<span id="AdiviséparB"/></p>
</div>
<div id="erreur">
<p style="color: red;">Une erreur s'est produite : <span id="msg"/></p>
</div>
</body>
</html>
- linhas 4-14: as opções da chamada Ajax;
- linha 10: a função JS a ser executada no início da solicitação. Essa função está definida no arquivo JS, referenciado na linha 24;
- linha 11: a função JS a ser executada em caso de falha da solicitação;
- linha 12: a função JS a ser executada caso a solicitação seja bem-sucedida;
- linha 13: a função JS a ser executada após a solicitação Ajax ter obtido seu resultado (falha ou sucesso);
- linhas 40-43: uma região com o ID [entete];
- linhas 44-49: uma região com o ID [résultats]. Ela exibirá os resultados das quatro operações aritméticas;
- linhas 50-52: uma região com o ID [erreur]. Ela exibirá uma eventual mensagem de erro.
7.4.2. A ação [Action02Post]
A solicitação Ajax é processada pela seguinte ação [Action02Post]:
[HttpPost]
public JsonResult Action02Post(FormCollection postedData, SessionModel session)
{
// simulação em espera
Thread.Sleep(2000);
// validação do modelo
ViewModel02 modèle = new ViewModel02();
// tempo de carregamento e cálculo
string HeureChargement = DateTime.Now.ToString("hh:mm:ss");
string HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
// atualização do modelo
TryUpdateModel(modèle, postedData);
if (!ModelState.IsValid)
{
// retornando um erro
return Json(new { Erreur = getErrorMessagesFor(ModelState), HeureCalcul = HeureCalcul });
}
// a cada duas vezes, simula-se um erro
int val = session.Randomizer.Next(2);
if (val == 0)
{
// retornamos um erro
return Json(new { Erreur = "[erreur aléatoire]", HeureCalcul = HeureCalcul });
}
// cálculos
string AplusB = string.Format("{0}", modèle.A + modèle.B);
string AmoinsB = string.Format("{0}", modèle.A - modèle.B);
string AmultipliéparB = string.Format("{0}", modèle.A * modèle.B);
string AdiviséparB = string.Format("{0}", modèle.A / modèle.B);
// os resultados são retornados
return Json(new { Erreur = "", AplusB = AplusB, AmoinsB = AmoinsB, AmultipliéparB = AmultipliéparB, AdiviséparB = AdiviséparB, HeureCalcul = HeureCalcul });
}
- linha 2: o método retorna um tipo [JsonResult], ou seja, um texto no formato JSON;
- linha 16: as informações são apresentadas na forma de uma instância de classe anônima serializada em JSON. O método [getErrorMessagesFor] já foi apresentado. A string JSON enviada ao navegador terá o seguinte formato:
- linha 31: mesmo procedimento para os resultados aritméticos. Desta vez, a string JSON enviada ao navegador terá o seguinte formato:
{"Erreur":"","AplusB":"4","AmoinsB":"-2","AmultipliéparB":"3","AdiviséparB":"0,333333333333333","HeureCalcul":"05:52:17"}
7.4.3. O código JavaScript do lado do cliente
Vale lembrar a configuração da chamada Ajax na página HTML enviada ao navegador do cliente:
AjaxOptions ajaxOpts = new AjaxOptions
{
HttpMethod = "post",
Url = Url.Action("Action02Post"),
LoadingElementId = "loading",
LoadingElementDuration = 1000,
OnBegin = "OnBegin",
OnFailure = "OnFailure",
OnSuccess = "OnSuccess",
OnComplete = "OnComplete"
};
As funções JS referenciadas nas linhas 7 a 10 (à direita do sinal =) estão definidas no seguinte arquivo [myScripts-02.js]:
// dados globais
var entete;
var loading;
var résultats;
var erreur;
var heureCalcul;
var msg;
var AplusB;
var AmoinsB;
var AmultipliéparB;
var AdiviséparB;
var formulaire;
...
function postForm() {
...
}
// ao carregar o documento
$(document).ready(function () {
formulaire = $("#formulaire");
entete = $("#entete");
loading = $("#loading");
erreur = $("#erreur");
résultats = $('#resultados');
heureCalcul = $("#heureCalcul");
msg = $("#msg");
AplusB = $("#AplusB");
AmoinsB = $("#AmoinsB");
AmultipliéparB = $("#AmultipliéparB");
AdiviséparB = $("#AdiviséparB");
// ocultamos alguns elementos da página
entete.hide();
résultats.hide();
erreur.hide();
});
// início
function OnBegin() {
....
}
// fim da solicitação
function OnComplete() {
...
}
// sucesso
function OnSuccess(data) {
....
}
// erro
function OnFailure(request, error) {
...
}
- linha 19: a função JS é executada ao final do carregamento da página no navegador;
- linhas 20-30: recuperam-se as referências de todos os componentes da página que nos interessam. A busca por um componente em uma página tem um custo e é preferível realizá-la apenas uma vez;
- linhas 33-35: os componentes [entete], [résultats] e [loading] são ocultados;
Ao iniciar a solicitação Ajax, a seguinte função é executada:
// início
function OnBegin() {
// luz de espera acesa
loading.show();
// ocultando alguns elementos da página
entete.hide();
résultats.hide();
erreur.hide();
}
- linha 4: o componente [loading] é exibido. Trata-se da imagem animada;
- linhas 6-8: os componentes [entete], [résultats] e [erreur] ficam ocultos;
Se a solicitação Ajax for bem-sucedida, executa-se o seguinte código JS:
// sucesso
function OnSuccess(data) {
// exibição dos resultados
heureCalcul.text(data.HeureCalcul);
entete.show();
if (data.Erreur != '') {
msg.text(data.Erreur);
erreur.show();
return;
}
// sem erro
AplusB.text(data.AplusB);
AmoinsB.text(data.AmoinsB);
AmultipliéparB.text(data.AmultipliéparB);
AdiviséparB.text(data.AdiviséparB);
résultats.show();
}
Para entender esse código, é preciso lembrar os dois textos JSON que podem ser enviados como resposta ao navegador:
em caso de erro; caso contrário, a sequência:
{"Erreur":"","AplusB":"4","AmoinsB":"-2","AmultipliéparB":"3","AdiviséparB":"0,333333333333333","HeureCalcul":"05:52:17"}
Se chamarmos essa sequência de [data], o valor do campo [Erreur] é obtido pela notação [data.Erreur] ou [data["Erreur"]], conforme a preferência. O mesmo vale para os demais campos da cadeia JSON. Além disso, para atribuir um texto sem formatação a um componente com ID X, escreve-se [X.text(chaine)]. Voltemos ao código da função [OnSuccess]:
- linha 2: [data] é a cadeia JSON recebida;
- linha 4: o componente [heureCalcul] recebe seu valor;
- linha 5: o componente [entete] é exibido;
- linha 6: teste do campo [Erreur] da cadeia JSON;
- linha 7: o componente [msg] recebe seu valor;
- linha 8: o componente [erreur] é exibido;
- linha 9: o processo de erro está concluído;
- linha 12: o componente [AplusB] recebe seu valor;
- linha 13: o componente [AmoinsB] recebe seu valor;
- linha 14: o componente [AmultipliéparB] recebe seu valor;
- linha 15: o componente [AdiviséparB] recebe seu valor;
- linha 16: o componente [résultats] é exibido.
A função [OnFailure] será executada caso a solicitação Ajax HTTP falhe. Essa falha é identificada pelo código HTTP retornado pelo servidor. O código 500 [Internal Server Error], por exemplo, indica que o servidor não conseguiu executar a solicitação. A função [OnFailure] é a seguinte:
// erro
function OnFailure(request, error) {
alert("L'erreur suivante s'est produite :" + error);
}
Limita-se a exibir uma caixa de diálogo com o erro que ocorreu. Na prática, seria necessário ser mais preciso. Em breve, proporemos outra solução.
Por fim, a função [OnComplete] é executada quando a consulta é concluída, seja com sucesso ou com falha.
// fim da consulta
function OnComplete() {
// sinal de espera desativado
loading.hide();
}
Vale lembrar que é a configuração da chamada Ajax na visualização [Action02Get.cshtml] que faz com que essas diferentes funções sejam chamadas:
AjaxOptions ajaxOpts = new AjaxOptions
{
...
OnBegin = "OnBegin",
OnFailure = "OnFailure",
OnSuccess = "OnSuccess",
OnComplete = "OnComplete"
};
7.4.4. O link [Calculer]
O código HTML do link [Calculer] na visualização [Action02Get.cshtml] é o seguinte:
<a href="javascript:postForm()">Calculer</a>
A função JS [postForm] foi encontrada no arquivo importado [myScripts-02.js]:
<script type="text/javascript" src="~/Scripts/myScripts-02.js"></script>
Seu código é o seguinte:
function postForm() {
// está sendo feita uma chamada Ajax manualmente com JQuery
$.ajax({
url: '/Premier/Action02Post',
type: 'POST',
data: formulaire.serialize(),
dataType: 'json',
beforeSend: OnBegin,
success: OnSuccess,
error: OnFailure,
complete: OnComplete
})
}
Já encontramos um código semelhante.
- linha 4: URL, destino da chamada Ajax;
- linha 5: comando HTTP utilizado pela chamada Ajax;
- linha 6: valores enviados. Eles são o resultado da serialização dos valores do formulário. Esse formulário, identificado pelo id [formulaire], é referenciado pela variável [formulaire]. [data] será uma sequência de caracteres no formato [A=val1&B=val2];
- linha 7: tipo de formatação da resposta esperada. Trata-se de uma sequência de caracteres JSON;
- linha 8: função JS a ser executada no início da chamada Ajax;
- linha 9: função JS a ser executada caso a chamada Ajax seja bem-sucedida;
- linha 10: função JS a ser executada caso a chamada Ajax falhe;
- linha 11: função JS a ser executada assim que a resposta do servidor for recebida, independentemente do resultado (sucesso ou erro).
Voltemos à função JavaScript que lida com o caso em que a chamada Ajax falha (linha 10). A chamada Ajax falha em diversas situações, por exemplo, quando o servidor retorna um código de erro como [403 Forbidden], [404 Not Found], [500 Internal Server Error], [301 Moved Permanently], ...
No exemplo anterior, a função [OnFailure] é a seguinte:
// erro
function OnFailure(request, error) {
alert("L'erreur suivante s'est produite :" + error);
}
Geralmente, a exibição do objeto [error] não fornece nenhuma informação relevante. Se for utilizada uma chamada Ajax feita com JQuery, é possível utilizar o seguinte método [OnFailure];
// erro
function OnFailure(jqXHR) {
alert("Erreur : " + jqXHR.status + " " + jqXHR.statusText);
msg.html(jqXHR.responseText);
erreur.show();
}
O objeto JQuery [jqXHR] possui, entre suas propriedades, as seguintes:
- responseText: o texto da resposta do servidor;
- status: o código de erro retornado pelo servidor;
- statusText: o texto associado a esse código de erro.
- linha 3: exibe-se o código de erro e a descrição correspondente;
- linha 4: insere-se a resposta HTML do servidor no componente de id [msg];
- linha 5: exibe-se a região com o ID [erreur].
Para testar essa função de erro, vamos criar artificialmente uma exceção na ação [Action02Post]:
[HttpPost]
public JsonResult Action02Post(FormCollection postedData, SessionModel session)
{
// uma exceção simulada para testar a função de erro da chamada Ajax
throw new Exception();
// simulação de espera
Thread.Sleep(2000);
// validação do modelo
...
A linha 5 lança uma exceção. Agora, vamos testar o aplicativo:
![]() |
Recebemos a seguinte resposta: [1] e [2]:
![]() |
A resposta do servidor nos permite identificar em qual linha do código do servidor ocorreu o erro. Muitas vezes, é uma informação útil de se saber. Daqui em diante, utilizaremos essa técnica para gerenciar os erros das chamadas Ajax.
7.5. Aplicativo web de página única
A tecnologia Ajax permite construir aplicativos de página única:
- a primeira página é resultante de uma solicitação convencional do navegador;
- as páginas seguintes são obtidas por meio de chamadas Ajax. Assim, no final, o navegador nunca muda de URL e nunca carrega uma nova página. Esse tipo de aplicativo é chamado de Aplicativo de Página Única (APU) ou, em inglês, Single Page Application (SPA).
Aqui está um exemplo básico desse tipo de aplicação. A nova aplicação terá duas visualizações:
![]() |
![]() |
- em [1], a ação [Action03Get] nos permite acessar a primeira página, a página 1;
- em [2], um link nos permite passar para a página 2 por meio de uma chamada Ajax;
- em [3], o URL não mudou. A página exibida é a página 2;
- em [4], um link permite que voltemos à página 1 por meio de uma chamada Ajax;
- em [5], o URL não sofreu alterações. A página exibida é a página 1.
O código da ação [Action03Get] é o seguinte:
[HttpGet]
public ViewResult Action03Get()
{
return View();
}
- linha 4: a visualização [Action03Get.cshtml] é exibida.
A visualização [Action03Get.cshtml] é a seguinte:
![]() |
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action03Get</title>
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
</head>
<body>
<h3>Ajax - 03 - Single Page Application</h3>
<div id="content">
@Html.Partial("Page1")
</div>
</body>
</html>
- linhas 16-18: um elemento com o ID [content]. É nesse elemento que as diferentes páginas serão exibidas;
- linha 17: por padrão, a página [Page1.cshtml] será exibida primeiro.
A página [Page1.cshtml] é a seguinte:
<h4>Page 1</h4>
<p>
@Ajax.ActionLink("Page 2", "Action04", new { Page = 2 }, new AjaxOptions() { UpdateTargetId = "content" })
</p>
- linha 1: o título da página para diferenciá-la da página 2;
- linha 3: um link Ajax com os seguintes parâmetros:
- o texto do link [Page 2];
- a ação de destino do link [Action04];
- os parâmetros da solicitação URL. Este será [/Premier/Action04?Page=2];
- as opções da chamada Ajax. Aqui, apenas o ID da região a ser atualizada com a resposta do servidor. Para as demais opções, são utilizados valores padrão, quando existentes. O método padrão de HTTP é GET.
Vamos ver o que acontece quando o link é clicado. A ação URL é solicitada com um [/Premier/Action04?Page=2]. A ação [Action04] é então executada:
[HttpGet]
public PartialViewResult Action04(string page = "1")
{
string vue = "Page1";
if (page == "2")
{
vue = "Page2";
}
return PartialView(vue);
}
- linha 2: a ação retorna um fluxo HTML parcial;
- linha 2: a ação tem como modelo a sequência [page]. Sabe-se, porém, que o URL contém esta informação: [/Premier/Action04?Page=2]. Vale lembrar que o modelo não diferencia maiúsculas de minúsculas;
- linhas 4-8: [vue] receberá o valor [Page2];
- linha 9: a visão parcial [Page2.cshtml] é retornada.
A visão parcial [Page2.cshtml] é a seguinte:
<h4>Page 2</h4>
<p>
@Ajax.ActionLink("Page 1", "Action04", new { Page = 1 }, new AjaxOptions() { UpdateTargetId = "content" })
</p>
O servidor, portanto, retorna o fluxo HTML acima como resposta à chamada Ajax GET [/Premier/Action04?Page=2]. Vale lembrar que essa chamada Ajax utiliza essa resposta para atualizar a região com o ID [content] (linha 3 abaixo):
<h4>Page 1</h4>
<p>
@Ajax.ActionLink("Page 2", "Action04", new { Page = 2 }, new AjaxOptions() { UpdateTargetId = "content" })
</p>
Isso resulta na exibição da seguinte nova página [1]:
![]() |
Seguindo o mesmo raciocínio, percebe-se que clicar no link [Page 1] de [1] fará com que seja exibido [2].
Voltemos ao esquema geral de um aplicativo ASP.NET MVC:
![]() |
Graças ao JavaScript incorporado nas páginas HTML e executado no navegador, é possível transferir código para o navegador e chegar à seguinte arquitetura:
![]() |
- em [1], a camada web ASP.NET MVC tornou-se uma interface web de acesso aos dados, geralmente armazenados em um banco de dados. As visualizações fornecidas contêm apenas dados e nenhum elemento de apresentação HTML, como, por exemplo, os fluxos XML ou JSON;
- no [2]: o navegador exibe visualizações estáticas (ou seja, não geradas dinamicamente) fornecidas por um servidor web que pode ou não estar na mesma máquina que o servidor [1]. Essas visualizações estáticas são, em seguida, enriquecidas com os dados obtidos pelo JavaScript a partir da interface web [1];
- o código JavaScript incorporado nas páginas HTML pode ser estruturado em camadas:
- a camada [présentation] lida com as interações com o usuário,
- a camada [DAO] é responsável pelo acesso aos dados por meio do servidor web [1],
- a camada [métier] corresponde à camada [métier], que anteriormente estava no servidor [1] e foi transferida para o navegador [2];
A vantagem dessa arquitetura é que ela envolve diferentes competências:
- o código do servidor web [1] requer competências em .NET, mas não em JavaScript, HTML, CSS;
- o código incorporado no navegador [2] requer competências em JavaScript, HTML, CSS, mas é independente da tecnologia do servidor web [1].
Assim, essa arquitetura facilita o trabalho em paralelo de equipes com competências diferentes. Ela se aplica particularmente bem a aplicativos de página única.
7.6. Aplicativo web de página única e validação no lado do cliente
Mencionamos anteriormente uma anomalia no exemplo Ajax-01. Recordamos o contexto:
![]() |
- em [1] e [2], foram inseridos valores inválidos. Eles são sinalizados pelos validadores do lado do cliente;
- no [3], clicou-se no link [Calculer];
- em [4], ocorreu um [POST], já que obtém-se a resposta [4].
Quando os valores são inválidos e clicamos no botão [Calculer], o [POST] para o servidor não ocorre. No mesmo caso, com o link [Calculer], o [POST] para o servidor é enviado. Portanto, há um comportamento do botão [Calculer] que não conseguimos reproduzir com o link [Calculer].
Vamos retomar esse exemplo em um novo contexto: o aplicativo terá várias visualizações e será do tipo [Application à Page Unique] que acabamos de descrever.
7.6.1. As visualizações do exemplo
O exemplo possui várias visualizações:
![]() |
- em [1], a visualização [Action05Get];
- em [2], a visualização parcial [Formulaire05];
- em [3], a vista parcial [Failure05];
![]() |
- em [4], a visualização parcial [Success05].
O aplicativo consiste em uma única página: ela é carregada pelo navegador na primeira solicitação. Em seguida, é atualizada por meio de chamadas Ajax.
As páginas anteriores são geradas pelas seguintes visualizações [cshtml]:
![]() |
A visualização carregada inicialmente é a seguinte: [Action05Get.cshtml]:
@model Exemple_04.Models.ViewModel05
@{
Layout = null;
}
<!DOCTYPE html>
<html lang="fr-FR">
<head>
<meta name="viewport" content="width=device-width" />
<title>Ajax-05</title>
<link rel="stylesheet" href="~/Content/Site.css" />
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/globalize.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.fr-FR.js"></script>
<script type="text/javascript" src="~/Scripts/globalize/cultures/globalize.culture.en-US.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script type="text/javascript" src="~/Scripts/myScripts-05.js"></script>
</head>
<body>
<h2>Ajax - 05, Page unique - Validation formulaire côté client</h2>
<p><strong>Heure de chargement : @Model.HeureChargement</strong></p>
<h4>Opérations arithmétiques sur deux nombres réels A et B positifs ou nuls</h4>
<img id="loading" style="display: none" src="~/Content/images/indicator.gif" />
<div id="content">
@Html.Partial("Formulaire05", Model)
</div>
</body>
</html>
Observe os seguintes pontos:
- linha 1: o modelo da visualização é do tipo [ViewModel05], que apresentaremos em breve;
- linhas 13-19: encontram-se os scripts JavaScript necessários para implementar Ajax e validação do lado do cliente;
- linha 20: vamos adicionar nossas próprias funções JavaScript no [myScripts-05.js];
- linha 27: a imagem animada de espera;
- linhas 28-30: uma tag com o ID [content]. É nessa tag que as visualizações parciais [Formulaire05, Success05, Failure05] serão inseridas;
- linha 29: inserção da vista parcial [Formulaire05].
A visualização [Action05Get] é responsável pela exibição da parte [1] da página inicial:
![]() |
A vista parcial [Formulaire05] irá gerar a parte [2] acima. Seu código é o seguinte:
@model Exemple_04.Models.ViewModel05
@using (Html.BeginForm("Action05Post", "Premier", FormMethod.Post, new { id = "formulaire" }))
{
<table>
<thead>
<tr>
<th>@Html.LabelFor(m => m.A)</th>
<th>@Html.LabelFor(m => m.B)</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Html.TextBoxFor(m => m.A)</td>
<td>@Html.TextBoxFor(m => m.B)</td>
</tr>
<tr>
<td>@Html.ValidationMessageFor(m => m.A)</td>
<td>@Html.ValidationMessageFor(m => m.B)</td>
</tr>
</tbody>
</table>
<p>
<table>
<tbody>
<tr>
<td><a href="javascript:calculer()">Calculer</a>
</td>
<td style="width: 20px" />
<td><a href="javascript:effacer()">Effacer</a>
</td>
</tr>
</tbody>
</table>
</p>
}
- linha 1: a vista parcial utiliza como modelo um tipo [ViewModel05];
- linha 3: o formulário gerado pelo método [Html.BeginForm]. Como esse formulário será enviado por uma chamada Ajax, os três primeiros parâmetros do método serão ignorados. A menos que o usuário tenha desativado o JavaScript em seu navegador. Ignoramos essa possibilidade aqui. O quarto parâmetro é importante. O formulário terá o id [formulaire];
- linhas 5-22: o formulário para inserção dos números A e B;
- linha 27: um link em JavaScript que inicia a execução das quatro operações aritméticas em A e B;
- linha 30: um link em JavaScript que apaga os dados inseridos e as eventuais mensagens de erro a eles associadas.
Observe-se que o formulário não possui um botão do tipo [submit]. Teremos que calcular manualmente o [Post] a partir dos valores A e B inseridos.
Se não houver erros, os resultados são exibidos:
![]() |
A parte [4] acima é gerada pela seguinte visão parcial [Success05.cshtml]:
@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h4>Résultats</h4>
<p>A+B=@Model.AplusB</p>
<p>A-B=@Model.AmoinsB</p>
<p>A*B=@Model.AmultipliéparB</p>
<p>A/B=@Model.AdiviséparB</p>
<p>
<a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>
- linha 1: a visão parcial [Success05.cshtml] recebe um modelo do tipo [ViewModel05];
- linha 12: um link em JavaScript para retornar às entradas.
Em caso de erro, é exibida outra vista parcial [3]:
![]() |
Essa visualização é gerada pelo código [Failure05.cshtml] a seguir:
@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
@foreach (string msg in Model.Erreurs)
{
<li>@msg</li>
}
</ul>
<p>
<a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>
- linha 1: a visão parcial [Failure05.cshtml] recebe um modelo do tipo [ViewModel05];
- linha 14: um link em JavaScript para retornar às entradas.
7.6.2. O modelo das visualizações
Todas as visualizações anteriores compartilham o mesmo modelo [ViewModel05]:
![]() |
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_04.Models
{
[Bind(Exclude = "AplusB, AmoinsB, AmultipliéparB, AdiviséparB, Erreurs, HeureChargement, HeureCalcul")]
public class ViewModel05
{
// formulário
[Required(ErrorMessage="Donnée A requise")]
[Display(Name="Valeur de A")]
[Range(0, Double.MaxValue, ErrorMessage = "Tapez un nombre A positif ou nul")]
public string A { get; set; }
[Required(ErrorMessage = "Donnée B requise")]
[Display(Name = "Valeur de B")]
[Range(0, Double.MaxValue, ErrorMessage="Tapez un nombre B positif ou nul")]
public string B { get; set; }
// resultados
public string AplusB { get; set; }
public string AmoinsB { get; set; }
public string AmultipliéparB { get; set; }
public string AdiviséparB { get; set; }
public List<string> Erreurs { get; set; }
public string HeureChargement { get; set; }
public string HeureCalcul { get; set; }
}
}
Trata-se do modelo [ViewModel01] já apresentado, com algumas pequenas diferenças:
- linhas 15 e 19: os campos A e B agora são do tipo [string] para exibir campos de preenchimento vazios, em vez de campos com o valor 0, na exibição inicial do formulário de preenchimento;
- linhas 14 e 18: isso não impede a verificação do valor inserido com um validador [Range];
- linha 26: uma lista de mensagens de erro exibida pela visualização [Failure05].
7.6.3. Os dados do escopo [Session]
No parágrafo 7.3.6, vimos que os dados da sessão estavam encapsulados no modelo [SessionModel] a seguir:
![]() |
using System;
namespace Exemple_03.Models
{
public class SessionModel
{
// o gerador de números aleatórios
public Random Randomizer { get; set; }
}
}
Esse modelo de sessão é ampliado para incluir os valores de A e B:
using System;
namespace Exemple_03.Models
{
public class SessionModel
{
// o gerador de números aleatórios
public Random Randomizer { get; set; }
// os valores de A e B
public string A { get; set; }
public string B { get; set; }
}
}
De fato, é necessário armazenar os valores de A e B na sessão, conforme mostra a sequência a seguir:
Solicitação 1
![]() |
Solicitação 2
![]() |
Em [4], encontramos as entradas feitas em [1]. No entanto, há duas solicitações distintas: HTTP. Sabe-se que a memória entre duas solicitações HTTP é a sessão. Para que a segunda solicitação possa recuperar os valores enviados pela primeira, é necessário que esses valores sejam armazenados na sessão.
7.6.4. A ação do servidor [Action05Get]
A ação [Action05Get] é a ação que exibe a página inicial única. Seu código é o seguinte:
[HttpGet]
public ViewResult Action05Get()
{
ViewModel05 modèle = new ViewModel05();
modèle.HeureChargement = DateTime.Now.ToString("hh:mm:ss");
return View(modèle);
}
- linha 6: a visualização [Action05Get.cshtml], já analisada, é exibida com um modelo do tipo [ViewModel05];
7.6.5. A ação do cliente [Calculer]
Vamos examinar as interações do usuário com as visualizações:
![]() |
O link [1] é um link em JavaScript:
<a href="javascript:calculer()">Calculer</a>
A função JavaScript [calculer] está no arquivo [myScripts-05.js]:
<script type="text/javascript" src="~/Scripts/myScripts-05.js"></script>
O código da função JavaScript [calculer] é o seguinte:
// dados globais
var content;
var loading;
function calculer() {
// primeiro, as referências no DOM
var formulaire = $("#formulaire");
// em seguida, validação do formulário
if (!formulaire.validate().form()) {
// formulário inválido – concluído
return;
}
// fazemos uma chamada Ajax manualmente
$.ajax({
url: '/Premier/Action05FaireCalcul',
type: 'POST',
data: formulaire.serialize(),
dataType: 'html',
beforeSend: function () {
loading.show();
},
success: function (data) {
content.html(data);
},
complete: function () {
loading.hide();
},
error: function (jqXHR) {
// exibição da resposta do servidor
content.html(jqXHR.responseText);
}
})
}
function retourSaisies() {
...
}
function effacer() {
...
}
// ao carregar o documento
$(document).ready(function () {
// recuperando as referências dos diferentes componentes da página
loading = $("#loading");
content = $("#content");
// armazenamos a imagem animada em cache
loading.hide();
});
- vale lembrar que o código JavaScript é sempre executado no lado do cliente, no navegador;
- linha 44: a função JS é executada quando o carregamento inicial da página única é concluído;
- linha 46: referência à imagem animada com id [loading];
- linha 47: referência à região com id [content]. É essa região que recebe as visualizações parciais [Formulaire05, Success05, Failure05];
- linhas 2-3: as variáveis das linhas 46-47 são declaradas globais para que as outras funções tenham acesso a elas. Há um custo associado à busca de elementos em uma página (linhas 46-47). Não há motivo para repetir essa busca se for possível evitá-la;
- linha 5: a função [calculer];
- linha 7: obtém-se uma referência ao formulário. A visão parcial [Formulaire05] atribuiu a ele o ID [formulaire];
- linha 9: esta instrução executa os validadores do formulário no lado do cliente. Era isso que faltava na anomalia constatada na página 183. Esse método é fornecido pela biblioteca [jquery.unobstrusive-ajax] utilizada pela página única:
<script type="text/javascript" src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
A instrução retorna [false] se o formulário for declarado inválido;
- linha 11: a chamada Ajax ao servidor não é feita se o formulário for inválido;
- linhas 14-32: a chamada Ajax é feita ao servidor;
- linha 15: o URL tem como destino a ação do servidor [Action05FaireCalcul];
- linha 16: ela é solicitada por meio de um [POST];
- linha 17: os valores enviados. São os dados inseridos no formulário, neste caso os valores de A e B;
- linhas 22-24: caso a chamada Ajax seja bem-sucedida, a função [calculer] atualiza a região de ID [content] com o fluxo HTML enviado pelo servidor.
Esse fluxo HTML é o enviado pela ação [Action05FaireCalcul], à qual a chamada Ajax se destina. O código dessa ação no lado do servidor é o seguinte:
[HttpPost]
public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
{
// modelo
ViewModel05 modèle = new ViewModel05();
// hora do cálculo
modèle.HeureCalcul = DateTime.Now.ToString("hh:mm:ss");
// atualização do modelo
TryUpdateModel(modèle, postedData);
if (!ModelState.IsValid)
{
// retorna um erro
modèle.Erreurs = getListOfMessagesFor(ModelState);
return PartialView("Failure05", modèle);
}
...
}
- linha 1: a ação aceita apenas um [post];
- linha 2: ela retorna uma visualização parcial;
- linha 2: ela recebe como parâmetros os valores enviados (postedData) e o modelo da sessão (session);
- linha 5: o modelo da visualização parcial é criado;
- linha 7: ele é atualizado com a hora do cálculo;
- linha 9: tenta-se aplicar os valores enviados ao modelo. Os validadores deste serão então executados. Pode-se questionar por que se dá esse trabalho, já que os validadores do lado do cliente impedem o POST caso os dados inseridos sejam inválidos. Na verdade, não temos certeza da origem do POST. Ele pode ter sido gerado por um código que não é o nosso. Por isso, devemos sempre realizar as verificações no lado do servidor;
- linha 10: verificamos se os validadores foram bem-sucedidos;
- linha 13: se o modelo for inválido, ele é atualizado com uma lista de erros. Não entraremos em detalhes sobre o método interno [getListOfMessagesFor], que é semelhante ao método [GetErrorMessagesFor] descrito na página 65;
- linha 14: a visão parcial [Failure05] é exibida com seu modelo. Repetimos o código dessa visão;
@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h2>Les erreurs suivantes se sont produites</h2>
<ul>
@foreach (string msg in Model.Erreurs)
{
<li>@msg</li>
}
</ul>
<p>
<a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>
- linhas 7-12: a lista de erros do modelo é exibida por meio da tag <ul>.
Lembre-se de que a função JS [calculer], naorigem do [Post] na ação do servidor [Action05FaireCalcul] irá inserir esse fluxo HTML na região de ID [content]. O resultado fica mais ou menos assim:
![]() |
Vamos continuar analisando o código da ação [Action05FaireCalcul]:
[HttpPost]
public PartialViewResult Action05FaireCalcul(FormCollection postedData, SessionModel session)
{
// modelo
ViewModel05 modèle = new ViewModel05();
...
// os valores de A e B são inseridos na sessão
session.A = modèle.A;
session.B = modèle.B;
// sem erros até o momento
List<string> erreurs = new List<string>();
// a cada duas vezes, simula-se um erro
int val = session.Randomizer.Next(2);
if (val == 0)
{
erreurs.Add("[erreur aléatoire]");
}
if (erreurs.Count != 0)
{
modèle.Erreurs = erreurs;
return PartialView("Failure05", modèle);
}
// cálculos
double A = double.Parse(modèle.A);
double B = double.Parse(modèle.B);
modèle.AplusB = string.Format("{0}", A + B);
modèle.AmoinsB = string.Format("{0}", A - B);
modèle.AmultipliéparB = string.Format("{0}", A * B);
modèle.AdiviséparB = string.Format("{0}", A / B);
// visualização
return PartialView("Success05", modèle);
}
- linha 7: o modelo foi declarado válido;
- linhas 8-9: os valores inseridos A e B são armazenados na sessão. O objetivo é poder recuperá-los na consulta que se seguirá;
- linhas 11-22: gera-se aleatoriamente um erro a cada duas vezes;
- linhas 24-29: realizam-se as quatro operações aritméticas nos números reais inseridos;
- linha 31: retorna-se a visão parcial [Success05] com seu modelo. Essa visão parcial é a seguinte:
@model Exemple_04.Models.ViewModel05
<hr />
<p><strong>Heure de calcul : @Model.HeureCalcul</strong></p>
<p>A=@Model.A</p>
<p>B=@Model.B</p>
<h4>Résultats</h4>
<p>A+B=@Model.AplusB</p>
<p>A-B=@Model.AmoinsB</p>
<p>A*B=@Model.AmultipliéparB</p>
<p>A/B=@Model.AdiviséparB</p>
<p>
<a href="javascript:retourSaisies()">Retour aux saisies</a>
</p>
Lembramos que a função JS [calculer],origem do [Post] na ação do servidor [Action05FaireCalcul] irá inserir esse fluxo HTML na região de ID [content]. O resultado é algo como isto:
![]() |
7.6.6. A ação do cliente [Effacer]
O link JavaScript [Effacer] permite restaurar o formulário ao seu estado inicial:
![]() | ![]() |
No formulário, o link JS [Effacer] está definido da seguinte forma:
<a href="javascript:effacer()">Effacer</a>
A função JS [effacer] está definida no arquivo [myScripts-05.js] da seguinte maneira:
// dados globais
var content;
var loading;
function calculer() {
...
}
function retourSaisies() {
...
}
function effacer() {
// primeiro as referências no DOM
var formulaire = $("#formulaire");
var A = $("#A");
var B = $("#B");
// atribuímos valores válidos aos dados inseridos
A.val("0");
B.val("0");
// em seguida, valida-se o formulário para que desapareçam
// as eventuais mensagens de erro
formulaire.validate().form();
// em seguida, atribui-se strings vazias aos campos de entrada
A.val("");
B.val("");
}
// ao carregar o documento
$(document).ready(function () {
// recuperam-se as referências dos diferentes componentes da página
loading = $("#loading");
content = $("#content");
// ocultamos a imagem animada
loading.hide();
});
- linhas 15-17: recuperam-se referências a diversos elementos do DOM (Document Object Model);
- linhas 19-20: insere-se valores válidos nos campos de entrada dos números A e B;
- linha 23: executam-se os validadores do lado do cliente. Como os valores de A e B são válidos, isso fará com que eventuais mensagens de erro que pudessem ser exibidas desapareçam;
- linhas 25-26: insere-se cadeias vazias nos campos de entrada dos números A e B;
7.6.7. A ação do cliente [Retour aux Saisies]
O link JavaScript [Retour aux Saisies] permite retornar ao formulário após obter os resultados:
![]() | ![]() |
No formulário, o link JS [Retour aux Saisies] está definido da seguinte forma:
<a href="javascript:retourSaisies()">Retour aux saisies</a>
A função JS [retourSaisies] está definida no arquivo [myScripts-05.js] da seguinte maneira:
// dados globais
var content;
var loading;
function calculer() {
...
}
function retourSaisies() {
// é feita uma chamada Ajax manualmente
$.ajax({
url: '/Premier/Action05RetourSaisies',
type: 'POST',
dataType: 'html',
beforeSend: function () {
loading.show();
},
success: function (data) {
content.html(data);
},
complete: function () {
loading.hide();
// IMPORTANT !! validação
$.validator.unobtrusive.parse($("#formulaire"));
},
error: function (jqXHR) {
content.html(jqXHR.responseText);
}
})
}
function effacer() {
...
}
// ao carregar o documento
$(document).ready(function () {
// recuperamos as referências dos diferentes componentes da página
loading = $("#loading");
content = $("#content");
// ocultamos a imagem animada
loading.hide();
});
- linhas 11-29: uma chamada Ajax;
- linha 12: o URL de destino;
- linha 13: será solicitada por um comando HTTP POST. Trata-se de um POST sem parâmetros enviados. Por isso, não encontramos uma linha do tipo:
na chamada Ajax;
- linha 14: o fluxo esperado do servidor é um fluxo HTML;
- linhas 18-20: esse fluxo HTML servirá para atualizar a região com o ID [content];
A ação do servidor [Action05RetourSaisies] é a seguinte:
[HttpPost]
public PartialViewResult Action05RetourSaisies(SessionModel session)
{
// visualização
return PartialView("Formulaire05", new ViewModel05() { A = session.A, B = session.B });
}
- linha 2: a ação recebe como parâmetro o modelo da sessão no qual armazenamos anteriormente os valores de A e B inseridos;
- linha 5: retorna-se a visualização parcial [Formulaire05] com um modelo do tipo [ViewModel05], no qual se tem o cuidado de inicializar os campos A e B com os valores de A e B obtidos da sessão;
Agora, voltemos ao código da função JavaScript [retourSaisies]:
function retourSaisies() {
// faz-se uma chamada Ajax manualmente
$.ajax({
url: '/Premier/Action05RetourSaisies',
type: 'POST',
dataType: 'html',
beforeSend: function () {
loading.show();
},
success: function (data) {
content.html(data);
},
complete: function () {
loading.hide();
// IMPORTANT !! validação
$.validator.unobtrusive.parse($("#formulaire"));
},
error: function (jqXHR) {
content.html(jqXHR.responseText);
}
})
}
- linha 13: o método executado quando a chamada Ajax é concluída;
- linha 14: a imagem animada de espera é ocultada;
- linha 16: uma instrução um pouco obscura para mim, encontrada na internet para resolver o seguinte problema: no formulário exibido pelo link [Retour aux saisies], os validadores do lado do cliente não estavam mais funcionando. Ao pesquisar informações sobre a biblioteca JS [jquery.unobtrusive-ajax], encontrei a solução da linha 16. Ela analisa o formulário, talvez para ativar os validadores do lado do cliente.
7.7. Tornar acessível na Internet um aplicativo ASP.NET
Consulte o parágrafo 9.26.
7.8. Geração de um aplicativo nativo para Android a partir de um aplicativo de página única APU
Consulte o parágrafo 9.27.

















































