8. A aplicação [SimuPaie] – versão 4 – ASP.NET / múltiplas visualizações / página única
Leituras recomendadas: referência [1], Desenvolvimento WEB com ASP.NET 1.1, parágrafos:
-
Componentes de servidor e controlador de aplicativo
-
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 estudada anteriormente, à qual foram adicionadas novas funcionalidades. A arquitetura da nossa aplicação evolui da seguinte forma:
![]() |
O processamento de uma solicitação de um cliente ocorre de acordo com as seguintes etapas:
- o cliente faz uma solicitação ao aplicativo.
- a aplicação processa essa solicitação. Para isso, ela pode precisar da ajuda da camada [métier], que, por sua vez, pode precisar da camada [dao] caso seja necessário trocar dados com o banco de dados. A aplicação recebe uma resposta da camada [métier].
- Com base nela, ela seleciona (3) a visualização (= a resposta) a ser enviada 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 – Visão – Controlador):
- [Application] é o controlador. Ele recebe todas as solicitações do cliente.
- [Saisies, Simulation, Simulations, ...] são as visualizações. Uma visualização em .NET consiste em código padrão ASP / HTML que contém componentes que devem ser inicializados. Os valores que devem ser fornecidos a esses componentes formam o modelo da vista.
Vamos implementar aqui o padrão de design (design pattern) MVC da seguinte maneira:
- as visualizações serão componentes [View] dentro de uma página única [Default.aspx]
- o controlador é, portanto, o código [Default.aspx.cs] dessa página única.
Apenas aplicativos básicos podem suportar essa implementação MVC. De fato, a cada solicitação, todos os componentes da página [Default.aspx] são instanciados, portanto, todas as visualizações. No momento de enviar a resposta, uma delas é escolhida pelo código de controle do aplicativo simplesmente tornando visível o componente [View] correspondente e ocultando os demais. Se a aplicação tiver muitas visualizações, a página [Default.aspx] terá muitos componentes e seu custo de instanciação pode se tornar proibitivo. Além disso, o modo [Design] da página corre o risco de se tornar incontrolável por ter visualizações em excesso. Esse tipo de arquitetura é adequado para aplicativos com poucas visualizações e desenvolvidos por uma única pessoa. Quando pode ser adotada, ela permite desenvolver uma arquitetura MVC de forma muito simples. É isso que veremos nesta nova versão.
8.1. As visualizações da aplicação
As diferentes visualizações apresentadas ao usuário serão as seguintes:
- a visualização [VueSaisies], que apresenta o formulário de simulação

- a visualização [VueSimulation], utilizada para exibir o resultado detalhado da simulação:

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

- a visualização [VueSimulationsVides], que indica que o cliente não possui ou não possui mais simulações:

- a visualização [VueErreurs], que indica um ou mais erros:

8.2. O projeto Visual Web Developer da camada [web]
O projeto do Visual Web Developer da camada [web] é o seguinte:
![]() |
- em [1], encontramos:
- o arquivo de configuração [Web.config] do aplicativo – é idêntico ao do aplicativo anterior.
- o arquivo [Global.cs], que gerencia os eventos do aplicativo web — neste caso, sua inicialização — é idêntico ao do aplicativo anterior, exceto pelo fato de que também gerencia o início da sessão do usuário.
- o formulário [Default.aspx] do aplicativo – contém as diferentes visualizações do aplicativo.
- No [2], encontram-se as referências do projeto – que são idênticas às da versão anterior
8.3. O arquivo [Global.cs]
O arquivo [Global.cs], que gerencia os eventos da aplicação web, é idêntico ao da aplicação anterior, exceto pelo fato de que ele também gerencia o início da sessão do usuário:
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 do aplicativo ---
public static Employe[] Employes;
public static string Msg = string.Empty;
public static bool Erreur = false;
public static IPamMetier PamMetier = null;
// inicialização do aplicativo
public void Application_Start(object sender, EventArgs e)
{
...
}
// inicialização da sessão de um usuário
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: gerenciamos o início da sessão. Nela, colocaremos a lista das simulações realizadas pelo usuário.
- linha 30: é criada uma lista vazia de simulações. Uma simulação é um objeto do tipo [Simulation], que detalharemos em breve.
- linha 31: a lista de simulações é inserida 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:

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 visualização. 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 tag para incluir as extensões Ajax
- linhas 11-73: o contêiner UpdatePanel atualizado por chamadas Ajax
- linhas 12-72: o conteúdo do contêiner UpdatePanel
- linhas 13-52: o cabeçalho que estará presente em cada visualização. Ele apresenta ao usuário a lista de ações possíveis na forma de uma lista de links.
- linha 33: observe-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 esse atributo está ausente, seu valor padrão é True. A validação da página poderá ser feita explicitamente no código executado no lado do servidor por meio da operação Page.Validate.
- linhas 53-57: um componente [MultiView] com uma única visualização [VueSaisies]. Por esse motivo, foi definido estaticamente, na linha 53, o número da visualização a ser exibida: ActiveViewIndex="0"
- linhas 53-67: um componente [MultiView] com quatro visualizações: a visualização [VueSimulation] nas linhas 59-61, a visualização [VueSimulations] nas linhas 62-64, a visualização [VueSimulationsVides] nas linhas 65-67, a visualização [VueErreurs] nas linhas 68-70. Um componente [MultiView] exibe apenas uma visualização por vez. Para exibir a visualização nº i do componente Vues2, deve-se escrever o código:
8.5.2. O en 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 | apaga o formulário de preenchimento | |
LinkButton | LinkButtonVoirSimulations | exibe a lista das simulações já realizadas | |
LinkButton | LinkButtonFormulaireSimulation | retorna ao formulário de preenchimento | |
LinkButton | LinkButtonEnregistrerSimulation | grava a simulação atual na lista de simulações | |
LinkButton | LinkButtonTerminerSession | encerra a sessão atual |
8.5.3. A visualização [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 visualização [Simulation]
O componente [View], denominado [VueSimulation], é o seguinte:

Ele é composto apenas por componentes [Label], dos quais os ID estão indicados acima.
8.5.5. A visualização [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 mouse no [GridView] / opção [Mise en forme automatique]
- em [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á vinculada (bound) a uma das propriedades públicas do objeto a ser exibido na linha do [GridView]. O objeto exibido aqui será um objeto do tipo [Simulation].
![]() |
- em [6]: forneça o título da coluna
- em [7]: especifique o nome da propriedade da classe [Simulation] que será associada a essa coluna.
- [DataFormatString] indica como os valores exibidos na coluna devem ser formatados.
As colunas do componente [GridViewSimulations] possuem as seguintes propriedades:
N.º | Propriedades |
Tipo: BoundField, HeaderText: Nome, DataField: Nome | |
Tipo: BoundField, HeaderText: Sobrenome, DataField: Nome | |
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: Adicionais, Campo de dados: Adicionais, 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 observar que o campo [DataField] deve corresponder a uma propriedade existente da classe [Simulation]. Ao 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 desenvolvedor que as define, como acabamos 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 acionará as verificações de validação que possam estar na página. De fato, a exclusão de uma simulação não requer nenhuma verificação de dados.
- em [4]: apenas o link de exclusão ficará visível.
- em [5]: o texto desse link
8.5.6. A visualização [SimulationsVides]
O componente [View], denominado [VueSimulationsVides], contém apenas texto:
8.5.7. A visualização [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. Esse 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: exibe o valor da expressão Container.DataItem, que designa o elemento atual da fonte de dados. Como esse elemento é um objeto, é o método ToString desse objeto que é utilizado para incluí-lo no fluxo HTML da página. Nossa coleção de objetos será uma coleção List(Of String) contendo mensagens de erro. As linhas 3 a 5 incluirão 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 do aplicativo:
![]() |
- [Default.aspx.cs], que é o código de controle da página única [Default.aspx], é o controlador do aplicativo.
- [Global] é o objeto do tipo [HttpApplication] que inicializa a aplicação e 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 exibidas as vistas solicitadas
// boolVues1: verdadeiro se a visualização múltipla Vues1 deve estar visível
// boolVues1: true se o multivista Vistas2 deve estar visível
// índice: índice da vista de Vues2 a ser exibida
...
}
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 da solicitação 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 usuário, apenas o evento Load das linhas 24 a 31 é processado. Nas solicitações seguintes (POST) feitas por meio dos links do menu, dois eventos são processados:
- o evento Load (24-31), mas a verificação do valor booleano Page.IsPostback (linha 27) faz com que nenhuma ação seja executada.
- o evento relacionado ao link que foi clicado:
![]() |
- linhas 33-36: processam o clique no link [1]
- linhas 38-41: processam o clique no link [2]
- linhas 43-46: processam o 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: processam o clique no link [6]
Para fatorar sequências de código que se repetem com frequência, foram criados dois métodos internos:
- setVues, linhas 7-14: define a(s) visualização(ões) a ser(em) exibida(s)
- setMenu, linhas 16-21: define as opções de menu a serem exibidas
8.6.2. O evento Load
Leituras recomendadas: referência [1], Desenvolvimento WEB com ASP.NET 1.1:
- parágrafo “Componente [Repeater] e vinculação de dados”.
A primeira tela apresentada ao usuário é a do formulário vazio:

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

O evento [Load] é tratado de forma análoga às versões anteriores ASP.NET:
// carregamento da página
protected void Page_Load(object sender, System.EventArgs e)
{
// processamento da solicitação inicial
if (!IsPostBack)
{
// erros de inicialização?
if (Global.Erreur)
{
// exibição da visualização [erreurs]
...
// posicionamento do menu
...
return;
}
// carregamento dos nomes dos funcionários na lista suspensa
...
// posicionamento do menu
...
// exibição da visualização [saisie]
...
}
}
Pergunta: complete o código acima
8.6.3. Ação: Realizar a simulação
A seguir, a tela marcada com (1) é a solicitação do usuário, e a tela marcada com (2) é a resposta enviada a ele pelo aplicativo web. A partir da tela inicial, o usuário pode iniciar uma simulação:


![]() |
O procedimento que processa essa 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 está válida — os dados inseridos são recuperados
double HeuresTravaillées = ...;
int JoursTravaillés = ...;
// calcula-se o salário do funcionário
FeuilleSalaire feuillesalaire;
try
{
feuillesalaire = ...
}
catch (PamException ex)
{
// exibição da visualização [erreurs]
...
return;
}
// o resultado é inserido na sessão
Session["simulation"] = ...
// exibição dos resultados
...
// exibição das visualizações [saisie, employe, salaire]
...
// exibição do menu
...
}
Pergunta: complete o código acima
8.6.4. Ação: Gravar a simulação
Após a simulação, o usuário pode solicitar seu registro:
![]() |

O procedimento que processa essa ação poderia ser semelhante ao seguinte:
protected void LinkButtonEnregistrerSimulation_Click(object sender, System.EventArgs e)
{
// salva-se a simulação atual na lista de simulações da sessão
...
// exibe a visualização [simulations]
...
}
Pergunta: complete 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 ]: a função do campo oculto _VIEWSTATE
Assim que a lista de simulações for exibida, o usuário pode solicitar o retorno ao formulário de simulação:
![]() |
Observe-se que a tela (2) exibe o formulário tal como foi preenchido. É importante lembrar que essas diferentes visualizações pertencem à mesma página. Entre as diferentes solicitações, os valores dos componentes são mantidos pelo mecanismo do ViewState, desde que esses componentes tenham sua propriedade definida entre EnableViewState e true.
O procedimento que processa essa 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: complete o código acima
8.6.6. Ação: Apagar a simulação
Ao retornar ao formulário de simulação, o usuário pode solicitar a exclusão das entradas existentes:
![]() |
O procedimento que processa essa 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 usuário pode solicitar a exibição das simulações que já realizou:
![]() |
![]() |
O procedimento que processa essa ação poderia ser semelhante ao seguinte:
protected void LinkButtonVoirSimulations_Click(object sender, System.EventArgs e)
{
// as simulações são recuperadas na sessão
...
// existem simulações?
if (...)
{
// visualização [simulations] visível
...
}
else
{
// visualização [SimulationsVides]
...
}
// definimos o menu
...
}
Pergunta: complete o código acima
8.6.8. Ação: Excluir uma simulação
O usuário pode solicitar a exclusão de uma simulação:
![]() |
![]() |
O procedimento [GridViewSimulations_RowDeleting ], que processa essa ação, poderia ser semelhante ao seguinte:
protected void GridViewSimulations_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
// recuperam-se as simulações da sessão
...
// exclui-se a simulação indicada (e.RowIndex é o número da linha excluída)
...
// Ainda há simulações?
if (...)
{
// preenche-se o GridView
...
}
else
{
// visualização [SimulationsVides]
...
}
}
Pergunta: complete o código acima
8.6.9. Ação: Encerrar a sessão
O usuário pode solicitar o encerramento de sua sessão de simulações. Isso descarta o conteúdo da sessão e exibe um formulário vazio:
![]() |
![]() |
O procedimento que processa essa ação poderia ser semelhante ao seguinte:
protected void LinkButtonTerminerSession_Click(object sender, System.EventArgs e)
{
// encerramos a sessão
...
// exibir a visualização [saisies]
...
// posicionamento do menu
...
}
Pergunta: complete o código acima
Exercício prático: implemente no computador o aplicativo web anterior. Adicione a ele um recurso Ajax.






















