8. A aplicação [SimuPaie] – versão 4 – ASP.NET / múltiplas vistas / página única
Leituras recomendadas: referência [1], Desenvolvimento WEB com ASP.NET 1.1, parágrafos:
-
Componentes de servidor e controlador de aplicações
-
Exemplos de aplicações MVC com componentes de servidor ASP
Analisamos agora uma versão derivada da aplicação ASP.NET de três camadas analisada anteriormente, à qual foram adicionadas novas funcionalidades. A arquitetura da nossa aplicação evolui da seguinte forma:
![]() |
O processamento de um pedido de um cliente decorre de acordo com as seguintes etapas:
- o cliente faz um pedido à aplicação.
- a aplicação processa essa solicitação. Para tal, pode necessitar da ajuda da camada [métier], que, por sua vez, pode necessitar da camada [dao] caso seja necessário trocar dados com a base de dados. A aplicação recebe uma resposta da camada [métier].
- Com base nessa resposta, a aplicação seleciona (3) a vista (= a resposta) a enviar ao cliente, fornecendo-lhe (4) as informações (o modelo) de que necessita.
- A resposta é enviada ao cliente (5)
Temos aqui uma arquitetura web denominada MVC (Modelo – Vista – Controlador):
- [Application] é o controlador. Este recebe todas as solicitações do cliente.
- [Saisies, Simulation, Simulations, ...] são as vistas. Uma vista em .NET consiste em código ASP / HTML padrão que contém componentes que devem ser inicializados. Os valores que devem ser fornecidos a estes componentes constituem o modelo da vista.
Vamos aqui implementar o padrão de design (design pattern) MVC da seguinte forma:
- as vistas serão componentes [View] dentro de uma página única [Default.aspx]
- o controlador é, então, o código [Default.aspx.cs] dessa página única.
Apenas aplicações básicas podem suportar esta implementação MVC. Com efeito, a cada pedido, todos os componentes da página [Default.aspx] são instanciados, ou seja, todas as vistas. No momento de enviar a resposta, uma delas é escolhida pelo código de controlo da aplicação, simplesmente tornando visível o componente [View] correspondente e ocultando os restantes. Se a aplicação tiver muitas vistas, a página [Default.aspx] terá muitos componentes e o seu custo de instanciação pode tornar-se proibitivo. Além disso, o modo [Design] da página corre o risco de se tornar incontrolável devido ao excesso de vistas. Este tipo de arquitetura é adequado para aplicações com poucas vistas e desenvolvidas por uma única pessoa. Quando pode ser adotada, permite desenvolver uma arquitetura MVC de forma muito simples. É isso que vamos ver nesta nova versão.
8.1. As vistas da aplicação
As diferentes vistas apresentadas ao utilizador serão as seguintes:
- a vista [VueSaisies], que apresenta o formulário de simulação

- a vista [VueSimulation] utilizada para apresentar o resultado detalhado da simulação:

- a vista [VueSimulations], que apresenta a lista das simulações realizadas pelo cliente

- a vista [VueSimulationsVides], que indica que o cliente não tem, ou já não tem, simulações:

- a vista [VueErreurs], que indica um ou mais erros:

8.2. O projeto Visual Web Developer da camada [web]
O projeto Visual Web Developer da camada [web] é o seguinte:
![]() |
- em [1] encontra-se:
- o ficheiro de configuração [Web.config] da aplicação – é idêntico ao da aplicação anterior.
- o ficheiro [Global.cs], que gere os eventos da aplicação web, neste caso o seu arranque, é idêntico ao da aplicação anterior, com a diferença de que também gere o início da sessão do utilizador.
- O formulário [Default.aspx] da aplicação – contém as diferentes vistas da aplicação.
- No ficheiro [2] encontram-se as referências do projeto – que são idênticas às da versão anterior
8.3. O ficheiro [Global.cs]
O ficheiro [Global.cs], que gere os eventos da aplicação web, é idêntico ao da aplicação anterior, com a única diferença de que gere adicionalmente o início da sessão do utilizador:
Global.cs
using System;
using System.Web;
using Pam.Dao.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;
using System.Collections.Generic;
using istia.st.pam.web;
namespace pam_v4
{
public class Global : HttpApplication
{
// --- dados estáticos da aplicação ---
public static Employe[] Employes;
public static string Msg = string.Empty;
public static bool Erreur = false;
public static IPamMetier PamMetier = null;
// início da aplicação
public void Application_Start(object sender, EventArgs e)
{
...
}
// início da sessão de um utilizador
public void Session_Start(object sender, EventArgs e)
{
// insere-se uma lista de simulações vazia na sessão
Session["simulations"] = new List<Simulation>();
}
}
}
- linhas 27-34: gere-se o início da sessão. Nesta, iremos incluir a lista das simulações realizadas pelo utilizador.
- linha 30: é criada uma lista de simulações vazia. Uma simulação é um objeto do tipo [Simulation], que iremos detalhar em breve.
- linha 31: a lista de simulações é colocada na sessão associada à chave «simulações»
8.4. A classe [Simulation]
Um objeto do tipo [Simulation] serve para encapsular uma linha da tabela de simulações:

O seu código é o seguinte:
namespace Pam.Web
{
public class Simulation
{
// dados de uma simulação
public string Nom { get; set; }
public string Prenom { get; set; }
public double HeuresTravaillees { get; set; }
public int JoursTravailles { get; set; }
public double SalaireBase { get; set; }
public double Indemnites { get; set; }
public double CotisationsSociales { get; set; }
public double SalaireNet { get; set; }
// construtores
public Simulation()
{
}
public Simulation(string nom, string prenom, double heuresTravailllees, int joursTravailles, double salaireBase, double indemnites, double cotisationsSociales, double salaireNet)
{
{
this.Nom = nom;
this.Prenom = prenom;
this.HeuresTravaillees = heuresTravailllees;
this.JoursTravailles = joursTravailles;
this.SalaireBase = salaireBase;
this.Indemnites = indemnites;
this.CotisationsSociales = cotisationsSociales;
this.SalaireNet = salaireNet;
}
}
}
}
Os campos da classe correspondem às colunas da tabela de simulações.
8.5. A página [Default.aspx]
8.5.1. Visão geral
A página [Default.aspx] contém vários componentes [View], um para cada vista. A sua estrutura é a seguinte:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="pam_v4.PagePam" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Simulateur de paie</title>
</head>
<body background="ressources/standard.jpg">
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
<asp:UpdatePanel runat="server" ID="UpdatePanelPam" UpdateMode="Conditional">
<ContentTemplate>
<table>
<tr>
<td>
<h2>
Simulateur de calcul de paie</h2>
</td>
<td>
<label>
 </label>
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
<img src="images/indicator.gif" />
<asp:Label ID="Label5" runat="server" BackColor="#FF8000"
EnableViewState="False" Text="Calcul en cours. Patientez ....">
</asp:Label>
</ProgressTemplate>
</asp:UpdateProgress>
</td>
<td>
<asp:LinkButton ID="LinkButtonFaireSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonFaireSimulation_Click">
| Faire la simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonEffacerSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonEffacerSimulation_Click">
| Effacer la simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonVoirSimulations" runat="server"
CausesValidation="False" OnClick="LinkButtonVoirSimulations_Click">
| Voir les simulations<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonFormulaireSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonFormulaireSimulation_Click">
| Retour au formulaire de simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonEnregistrerSimulation" runat="server"
CausesValidation="False" OnClick="LinkButtonEnregistrerSimulation_Click">
| Enregistrer la simulation<br /></asp:LinkButton>
<asp:LinkButton ID="LinkButtonTerminerSession" runat="server"
CausesValidation="False" OnClick="LinkButtonTerminerSession_Click">
| Terminer la session<br /></asp:LinkButton>
</td>
</table>
<hr />
<asp:MultiView ID="Vues1" ActiveViewIndex="0" runat="server">
<asp:View ID="VueSaisies" runat="server">
...
</asp:View>
</asp:MultiView>
<asp:MultiView ID="Vues2" runat="server">
<asp:View ID="VueSimulation" runat="server">
...
</asp:View>
<asp:View ID="VueSimulations" runat="server">
...
</asp:View>
<asp:View ID="VueSimulationsVides" runat="server">
...
</asp:View>
<asp:View ID="VueErreurs" runat="server">
...
</asp:View>
</asp:MultiView>
</ContentTemplate>
</asp:UpdatePanel>
</form>
</body>
</html>
- linha 10: a baliza para implementar as extensões Ajax
- linhas 11-73: o contentor UpdatePanel atualizado por chamadas Ajax
- linhas 12-72: o conteúdo do contentor UpdatePanel
- linhas 13-52: o cabeçalho que estará presente em cada vista. Apresenta ao utilizador a lista de ações possíveis sob a forma de uma lista de links.
- linha 33: note-se o atributo CausesValidation="False", que faz com que os validadores da página não sejam executados implicitamente quando o link for clicado. Quando este atributo está ausente, o seu valor por predefinição é True. A validação da página poderá ser efetuada explicitamente no código executado no lado do servidor através da operação Page.Validate.
- linhas 53-57: um componente [MultiView] com uma única vista [VueSaisies]. Por este motivo, fixou-se na linha 53 o número da vista a apresentar: ActiveViewIndex="0"
- linhas 53-67: um componente [MultiView] com quatro vistas: a vista [VueSimulation] nas linhas 59-61, a vista [VueSimulations] nas linhas 62-64, a vista [VueSimulationsVides] nas linhas 65-67, a vista [VueErreurs] nas linhas 68-70. Um componente [MultiView] apresenta apenas uma vista de cada vez. Para apresentar a vista n.º i do componente Vues2, deve escrever-se o código:
8.5.2. O en o cabeçalho
O cabeçalho é composto pelos seguintes componentes:
![]() |
N.º | Tipo | Nome | Função |
LinkButton | LinkButtonFaireSimulation | solicita o cálculo da simulação | |
LinkButton | LinkButtonEffacerSimulation | limpa o formulário de introdução de dados | |
LinkButton | LinkButtonVoirSimulations | exibe a lista das simulações já realizadas | |
LinkButton | LinkButtonFormulaireSimulation | volta ao formulário de introdução de dados | |
LinkButton | LinkButtonEnregistrerSimulation | regista a simulação atual na lista de simulações | |
LinkButton | LinkButtonTerminerSession | encerra a sessão atual |
8.5.3. A vista [Saisies]
O componente [View], denominado [VueSaisies], é o seguinte:
![]() |
N.º | Tipo | Nome | Função |
DropDownList | ComboBoxEmployes | Contém a lista dos nomes dos funcionários | |
TextBox | TextBoxHeures | Número de horas trabalhadas – número real | |
TextBox | TextBoxJours | Número de dias trabalhados – número inteiro | |
RequiredFieldValidator | RequiredFieldValidatorHeures | verifica se o campo [2] [TextBoxHeures] não está vazio | |
RegularExpressionValidator | RegularExpressionValidatorHeures | verifica se o campo [2] [TextBoxHeures] é um número real >=0 | |
RequiredFieldValidator | RequiredFieldValidatorJours | verifica se o campo [3] [TextBoxJours] não está vazio | |
RegularExpressionValidator | RegularExpressionValidatorJours | verifica se o campo [3] [TextBoxJours] é um número inteiro >=0 |
8.5.4. A vista [Simulation]
O componente [View] denominado [VueSimulation] é o seguinte:

É composto apenas por componentes [Label], dos quais os ID estão indicados acima.
8.5.5. A vista [Simulations]
O componente [View], denominado [VueSimulations], é o seguinte:
![]() |
N.º | Tipo | Nome | Função |
GridView | GridViewSimulations | Contém a lista de simulações |
As propriedades do componente [GridViewSimulations] foram definidas da seguinte forma:
![]() |
- em [1]: clique com o botão direito do rato no [GridView] / opção [Mise en forme automatique]
- para [2]: selecionar um tipo de exibição para o [GridView]
![]() |
- em [3]: selecionar as propriedades do [GridView]
- em [4]: editar as colunas do [GridView]
- em [5]: adicionar uma coluna do tipo [BoundField] que será associada (bound) a uma das propriedades públicas do objeto a apresentar na linha do [GridView]. O objeto aqui apresentado será um objeto do tipo [Simulation].
![]() |
- em [6]: indicar o título da coluna
- em [7]: indicar o nome da propriedade da classe [Simulation] que será associada a esta coluna.
- [DataFormatString] indica como devem ser formatados os valores apresentados na coluna.
As colunas do componente [GridViewSimulations] têm as seguintes propriedades:
N.º | Propriedades |
Tipo: BoundField, HeaderText: Nome, DataField: Nome | |
Tipo: BoundField, HeaderText: Apelido, DataField: Nome próprio | |
Tipo: BoundField, HeaderText: Horas trabalhadas, DataField: HeuresTravaillees | |
Tipo: BoundField, HeaderText: Dias trabalhados, DataField: JoursTravailles | |
Tipo: BoundField, HeaderText: Salário base, DataField: SalaireBase, DataFormatString: {0:C} (formato monetário, C = Moeda) – exibirá o símbolo do euro. | |
Tipo: BoundField, HeaderText: Subsídios, Campo de dados: Indemnites, DataFormatString: {0:C} | |
Tipo: BoundField, HeaderText: Contribuições sociais, DataField: CotisationsSociales, DataFormatString: {0:C} | |
Tipo: BoundField, HeaderText: Salário líquido, DataField: SalaireNet, DataFormatString: {0:C} |
É importante ter em conta que o campo [DataField] deve corresponder a uma propriedade existente da classe [Simulation]. No final desta fase, todas as colunas do tipo [BoundField] foram criadas:
![]() |
- em [1]: as colunas criadas para o [GridView]
- em [2]: a geração automática das colunas deve ser desativada quando é o próprio programador que as define, tal como acabámos de fazer.
Resta-nos criar a coluna dos links [Retirer]:

![]() |
- em [1]: adicionar uma coluna do tipo [CommandField / Supprimer]
- em [2]: ButtonType=Link para ter um link na coluna em vez de um botão
- em [3]: CausesValidation=False; um clique no link não provocará a execução das verificações de validação que possam existir na página. Com efeito, a eliminação de uma simulação não requer qualquer verificação de dados.
- em [4]: apenas o link de eliminação ficará visível.
- em [5]: o texto deste link
8.5.6. A vista [SimulationsVides]
O componente [View], denominado [VueSimulationsVides], contém apenas texto:
8.5.7. A vista [Erreurs]
O componente [View], denominado [VueErreurs], é o seguinte:
![]() |
N.º | Tipo | Nome | Função |
Repetidor | RptErreurs | exibe uma lista de mensagens de erro |
O componente [Repeater] permite repetir um código ASP.NET / HTML para cada objeto de uma fonte de dados, geralmente uma coleção. Este código é definido diretamente no código-fonte ASP.NET da página:
<asp:Repeater ID="RptErreurs" runat="server">
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
</asp:Repeater>
- linha 2: <ItemTemplate> define o código que será repetido para cada elemento da fonte de dados.
- linha 4: apresenta o valor da expressão Container.DataItem, que designa o elemento atual da fonte de dados. Sendo este elemento um objeto, é o método ToString desse objeto que é utilizado para o incluir no fluxo HTML da página. A nossa coleção de objetos será uma coleção List(Of String) que contém mensagens de erro. As linhas 3-5 irão incluir sequências <li>Message</li> no fluxo HTML da página.
8.6. O controlador [Default.aspx.cs]
8.6.1. Visão geral
Voltemos à arquitetura MVC da aplicação:
![]() |
- [Default.aspx.cs], que é o código de controlo da página única [Default.aspx], é o controlador da aplicação.
- [Global] é o objeto do tipo [HttpApplication] que inicializa a aplicação e que possui uma referência à camada [métier].
A estrutura do código do controlador [Default.aspx.cs] é a seguinte:
using System.Collections.Generic;
...
public partial class PagePam : Page
{
private void setVues(bool boolVues1, bool boolVues2, int index)
{
// são apresentadas as vistas solicitadas
// boolVues1: true se a vista múltipla Vues1 tiver de estar visível
// boolVues1: true se o conjunto de vistas «Vistas2» deve estar visível
// índice: índice da vista de Vues2 a apresentar
...
}
private void setMenu(bool boolFaireSimulation, bool boolEnregistrerSimulation, bool boolEffacerSimulation, bool boolFormulaireSimulation, bool boolVoirSimulations, bool boolTerminerSession)
{
// definem-se as opções do menu
// cada valor booleano é atribuído à propriedade Visível do link correspondente
...
}
// carregamento da página
protected void Page_Load(object sender, System.EventArgs e)
{
// processamento do pedido inicial
if (!IsPostBack)
{
...
}
}
protected void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonEffacerSimulation_Click(object sender, System.EventArgs e)
{
....
}
protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
{
...
}
protected void LinkButtonFormulaireSimulation_Click(object sender, System.EventArgs e)
{
...
}
protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
...
}
}
Na solicitação inicial (GET) do utilizador, apenas o evento Load das linhas 24-31 é processado. Nas solicitações seguintes (POST) efetuadas através dos links do menu, são processados dois eventos:
- o evento Load (24-31), mas o teste do valor booleano Page.IsPostback (linha 27) faz com que nada seja executado.
- o evento associado ao link em que se clicou:
![]() |
- linhas 33-36: processam o clique no link [1]
- linhas 38-41: tratam do clique no link [2]
- linhas 43-46: tratam do clique no link [3]
- linhas 58-61: processam o clique no link [4]
- linhas 48-51: processam o clique no link [5]
- linhas 53-56: tratam o clique no link [6]
Para factorizar sequências de código que se repetem frequentemente, foram criados dois métodos internos:
- setVues, linhas 7-14: define a(s) vista(s) a apresentar
- setMenu, linhas 16-21: define as opções de menu a apresentar
8.6.2. O evento Load
Leituras recomendadas: referência [1], Desenvolvimento WEB com ASP.NET 1.1:
- parágrafo «Componente [Repeater] e ligação de dados».
A primeira vista apresentada ao utilizador é a do formulário vazio:

A inicialização da aplicação requer o acesso a uma fonte de dados, o que pode falhar. Nesse caso, a primeira página é uma página de erros:

O evento [Load] é tratado de forma análoga à das versões anteriores ASP.NET:
// carregamento da página
protected void Page_Load(object sender, System.EventArgs e)
{
// processamento do pedido inicial
if (!IsPostBack)
{
// erros de inicialização?
if (Global.Erreur)
{
// exibição da vista [erreurs]
...
// posicionamento do menu
...
return;
}
// carregamento dos nomes dos funcionários na lista suspensa
...
// posicionamento do menu
...
// visualização da vista [saisie]
...
}
}
Pergunta: completar o código acima
8.6.3. Ação: Efetuar a simulação
A seguir, o ecrã marcado com (1) corresponde ao pedido do utilizador, enquanto o ecrã marcado com (2) corresponde à resposta que lhe é enviada pela aplicação web. A partir do ecrã inicial, o utilizador pode iniciar uma simulação:


![]() |
O procedimento que processa esta ação poderia ser semelhante ao seguinte:
protected void LinkButtonFaireSimulation_Click(object sender, System.EventArgs e)
{
// cálculo do salário
// página válida?
Page.Validate();
if (!Page.IsValid)
{
// visualização da página [saisie]
...
return;
}
// a página é válida - recuperam-se os dados introduzidos
double HeuresTravaillées = ...;
int JoursTravaillés = ...;
// calcula-se o salário do funcionário
FeuilleSalaire feuillesalaire;
try
{
feuillesalaire = ...
}
catch (PamException ex)
{
// exibição da vista [erreurs]
...
return;
}
// o resultado é colocado na sessão
Session["simulation"] = ...
// exibição dos resultados
...
// exibição das vistas [saisie, employe, salaire]
...
// exibição do menu
...
}
Pergunta: completar o código acima
8.6.4. Ação: Guardar a simulação
Depois de concluída a simulação, o utilizador pode solicitar o seu registo:
![]() |

O procedimento que processa esta ação poderia ser semelhante ao seguinte:
protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
{
// guarda-se a simulação atual na lista de simulações da sessão
...
// exibe a vista [simulations]
...
}
Pergunta: completar o código acima
8.6.5. Ação: Voltar ao formulário de simulação
Leituras recomendadas: referência [1], [Développement WEB avec ASP.NET 1.1 ]: o papel do campo oculto _VIEWSTATE
Assim que a lista de simulações for apresentada, o utilizador pode solicitar o regresso ao formulário de simulação:
![]() |
Note-se que o ecrã (2) apresenta o formulário tal como foi preenchido. É importante recordar que estas diferentes vistas pertencem à mesma página. Entre as diferentes consultas, os valores dos componentes são mantidos pelo mecanismo do ViewState, desde que esses componentes tenham a sua propriedade definida entre EnableViewState e true.
O procedimento que processa esta ação poderia ser semelhante ao seguinte:
protected void LinkButtonFormulaireSimulation_Click(object sender, System.EventArgs e)
{
// exibição da vista [saisie]
...
// posicionamento do menu
...
}
Pergunta: completar o código acima
8.6.6. Ação: Apagar a simulação
Ao regressar ao formulário de simulação, o utilizador pode solicitar a eliminação dos dados introduzidos:
![]() |
O procedimento que processa esta ação poderia ser semelhante ao seguinte:
protected void LinkButtonEffacerSimulation_Click(object sender, System.EventArgs e)
{
// RAZ do formulário
...
}
Pergunta: completar o código acima
8.6.7. Ação: Ver as simulações
O utilizador pode solicitar a visualização das simulações que já realizou:
![]() |
![]() |
O procedimento que processa esta ação poderia ser semelhante ao seguinte:
protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
{
// recuperam-se as simulações na sessão
...
// existem simulações?
if (...)
{
// vista [simulations] visível
...
}
else
{
// visualização [SimulationsVides]
...
}
// define-se o menu
...
}
Pergunta: completar o código acima
8.6.8. Ação: Eliminar uma simulação
O utilizador pode solicitar a eliminação de uma simulação:
![]() |
![]() |
O procedimento [GridViewSimulations_RowDeleting ] que processa esta ação poderá ser semelhante ao seguinte:
protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// recuperam-se as simulações na sessão
...
// elimina-se a simulação indicada (e.RowIndex é o n.º da linha eliminada)
...
// Ainda existem simulações?
if (...)
{
// preenche-se o GridView
...
}
else
{
// visualização [SimulationsVides]
...
}
}
Pergunta: complete o código acima
8.6.9. Ação: Terminar a sessão
O utilizador pode solicitar o encerramento da sua sessão de simulações. Isto descarta o conteúdo da sua sessão e apresenta um formulário em branco:
![]() |
![]() |
O procedimento que processa esta ação poderá ser semelhante ao seguinte:
protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
{
// encerra-se a sessão
...
// exibir a vista [saisies]
...
// posicionamento do menu
...
}
Pergunta: complete o código acima
Trabalho prático: implementar na máquina a aplicação web anterior. Adicione-lhe um comportamento Ajax.






















