5. A visualização e seu modelo
5.1. Introduction
Voltemos à arquitetura de uma aplicação ASP.NET MVC:
![]() |
No capítulo anterior, estudamos como ASP.NET MVC apresentava as informações da consulta [1] a uma ação [2a] na forma de um modelo que podia conter restrições de validação. Esse modelo era fornecido como entrada para a ação e nós o chamamos de modelo da ação. Agora, vamos nos concentrar no resultado mais comum de uma ação: o tipo [ViewResult], que corresponde a uma vista V [3] acompanhada de seu modelo M [2c]. Esse modelo será chamado de modelo da visualização V, que não deve ser confundido com o modelo da ação que acabamos de estudar. Um é a entrada da ação, o outro é a saída.
Vamos começar criando um novo projeto [Exemple-03] [1], ainda dentro da mesma solução, do tipo básico ASP.NET MVC:
![]() |
Vamos criar um controlador chamado [First] [2]. O código gerado para esse controlador é o seguinte:
using System.Web.Mvc;
namespace Exemple_03.Controllers
{
public class FirstController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
- linhas 7-10: foi criada uma ação [Index]. O tipo do resultado do método [Index] é o da classe [ActionResult], da qual derivam a maioria dos resultados possíveis de uma ação;
- linha 9: o método [View] da classe [Controller] (linha 5) retorna um tipo [ViewResult], que deriva de [ActionResult]. Esse método admite várias sobrecargas. Veremos algumas delas. A principal é a seguinte:
![]() |
- o primeiro parâmetro é o nome da visualização. Se estiver ausente, a visualização utilizada será aquela que possui o mesmo nome da ação que gera o [ViewResult] e que será procurada na pasta [/Views/{controller}], onde {controller} é o nome do controlador;
- o segundo é o modelo da vista. Se estiver ausente, a vista não possui modelo.
O método [Index] abaixo:
public ActionResult Index()
{
return View();
}
solicita que a vista [/Views/First/Index.cshtml] seja exibida. Ele não transmite nenhum modelo a ela. Vamos criar [1] na pasta [/Views/First]:
![]() |
e, em seguida, vamos criar nela a visualização [Index] [2]:
![]() |
![]() |
Especifica-se o nome da visualização como [3]. Ela é criada como [4]. O código gerado é o seguinte:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
</div>
</body>
</html>
Trata-se de um HTML clássico, exceto pelas linhas 1 a 3, que são código C#. O programa que gerencia as visualizações é chamado de mecanismo de visualizações. Ele se encarrega de gerenciar tudo o que não é HTML para transformá-lo em HTML. No final das contas, é isso que será enviado ao cliente. O mecanismo de visualização, neste caso, é chamado de [Razor]. Ele permite incluir código C# em uma visualização. O [Razor] interpretará esse código C# e, a partir dele, produzirá código HTML. Aqui estão algumas regras básicas para a inclusão de código C# em uma visualização:
- a transição do HTML para o C# ocorre ao encontrar o caractere @ (linha 1). Se esse caractere introduzir um bloco de código, colocar-se-ão as chaves (linhas 1 e 3). Se introduzir uma variável cujo valor se deseja recuperar, escrever-se-á simplesmente @variável;
- a transição do C# para o HTML ocorre ao encontrar o caractere < (linha 5). Às vezes, é necessário forçar essa conversão, principalmente quando se inclui na página texto bruto sem a tag HTML. Nesse caso, utilizar-se-á a tag <text> para inserir o texto: <text>aqui vai o texto bruto</text>.
A linha 2 acima indica que a visualização [Index] não possui uma página mestre.
Vamos modificar a visualização da seguinte maneira:
@{
Layout = null;
string vue = "Index";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<h3>Vue @vue</h3>
</div>
</body>
</html>
- linha 3: define uma variável C#;
- linha 15: exibe o valor dessa variável.
Vamos agora acessar a URL [/First/Index]:
![]() |
O código HTML recebido é o seguinte:
Trata-se de um documento HTML puro. Todo o código C# desapareceu.
5.2. Usar o [ViewBag] para passar informações para a visualização
Criamos uma nova ação chamada [Action01] associada à visualização [Action01.cshtml]:
![]() |
A ação [Action01] é a seguinte:
// Ação01
public ViewResult Action01()
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View();
}
- linha 4: utiliza-se a propriedade [ViewBag] do controlador. Trata-se de um objeto dinâmico ao qual é possível adicionar propriedades, conforme feito na linha 4. Esse objeto tem a particularidade de também ser acessível à visualização. Portanto, essa é uma forma de transmitir informações a ele;
- linha 5: a visualização padrão da ação é solicitada. Trata-se da visualização [/First/Action01.cshtml]. Nenhum modelo é transmitido a ela.
A visualização [Action01.cshtml] é a seguinte:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action01</title>
</head>
<body>
<div>
<h4>@ViewBag.info</h4>
</div>
</body>
</html>
- linha 14: a propriedade [ViewBag.info] é exibida.
Vamos testar. Solicitamos o URL [/First/Action01]:
![]() |
5.3. Usar um modelo fortemente tipado para passar informações para a visualização
O método anterior tem a desvantagem de não permitir a detecção de erros antes da execução. Assim, se a visualização [Action01.cshtml] utilizar o código
<h4>@ViewBag.Info</h4>
, ocorrerá um erro, pois a propriedade [Info] não existe. A propriedade criada pela ação [Action01] é chamada [info]. É possível, então, utilizar um modelo fortemente tipado para evitar esse inconveniente.
Em um dos exemplos analisados anteriormente, a ação era a seguinte:
// Ação10
public ContentResult Action10(ActionModel03 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("email={0}, jour={1}, info1={2}, info2={3}, info3={4}, erreurs={5}",
modèle.Email, modèle.Jour, modèle.Info1, modèle.Info2, modèle.Info3, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
A ação [Action10] transmitia ao seu cliente seis informações (e-mail, dia, Info1, Info2, Info3, erros) na forma de uma sequência de caracteres. Vamos transmitir essas informações em um modelo de visualização [ViewModel01]. Como esse modelo herda informações de [ActionModel03], vamos derivá-lo dessa classe.
Começamos copiando o [ActionModel03] do projeto [Exemple-02] para o projeto [Exemple-03] atual:
![]() |
e alteramos seu espaço de nomes para que seja o mesmo do projeto [Exemple-03]:
using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
public class ActionModel03
{
[Required(ErrorMessage = "Le paramètre email est requis")]
[EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
public string Email { get; set; }
[Required(ErrorMessage = "Le paramètre jour est requis")]
[RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
public string Jour { get; set; }
[Required(ErrorMessage = "Le paramètre info1 est requis")]
[MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
public string Info1 { get; set; }
[Required(ErrorMessage = "Le paramètre info2 est requis")]
[MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
public string Info2 { get; set; }
[Required(ErrorMessage = "Le paramètre info3 est requis")]
[MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
[MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
public string Info3 { get; set; }
}
}
- linha 2: o novo espaço de nomes;
Em seguida, criamos a classe [ViewModel01]:
![]() |
O código de [ViewModel01] é o seguinte:
namespace Exemple_03.Models
{
public class ViewModel01 : ActionModel03
{
public string Erreurs { get; set; }
}
}
- linha 3: a classe herda de [ActionModel03] e, portanto, das propriedades de [Email, Jour, Info1, Info2, Info3];
- linha 5: adiciona-se a propriedade [Erreurs].
Agora, escrevemos a ação [Action02], que:
- aceita como entrada o modelo de ação [ActionModel03];
- e gera como saída o modelo de visualização [ViewModel01].
Seu código é o seguinte:
// Ação02
public ViewResult Action02(ActionModel03 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
return View(new ViewModel01(){Email=modèle.Email, Jour=modèle.Jour, Info1=modèle.Info1, Info2=modèle.Info2, Info3=modèle.Info3, Erreurs=erreurs});
}
- linha 1: [Action02] recebe o modelo de ação [ActionModel03]. Ela retorna um resultado do tipo [ViewResult];
- linha 4: os erros relacionados ao modelo de ação [ActionModel03] são agregados na sequência de caracteres [erreurs]. O método [getErrorMessagesFor] foi descrito na página 65 e foi incluído no controlador [First] do novo projeto;
- linha 5: o método [View] é chamado com um parâmetro. Esse parâmetro é o modelo da visualização. A visualização não é especificada. Portanto, será utilizada a visualização padrão [/Views/First/Action02]. O modelo da vista [ViewModel01] é instanciado e inicializado com as cinco informações do modelo de ação [ActionModel03] e a informação [erreurs] construída na linha 4.
Agora, construímos a visualização [/First/Action02.cshtml]:
![]() |
Seu código é o seguinte:
@model Exemple_03.Models.ViewModel01
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action02</title>
</head>
<body>
<h3>Informations du modèle de vue</h3>
<ul>
<li>Email : @Model.Email</li>
<li>Jour : @Model.Jour</li>
<li>Info1 : @Model.Info1</li>
<li>Info2 : @Model.Info2</li>
<li>Info3 : @Model.Info3</li>
<li>Erreurs : @Model.Erreurs</li>
</ul>
</body>
</html>
- A novidade está na linha 1. A notação [@model] define o tipo do modelo da visualização. Esse modelo é, em seguida, referenciado pela notação [@Model] (linhas 16-21);
- linhas 15 a 22: as informações do modelo são exibidas em uma lista.
Vejamos alguns exemplos de execução da ação [Action02].
Primeiro, sem parâmetros:
![]() |
depois com parâmetros incorretos:
![]() |
e, por fim, com parâmetros corretos:
![]() |
Neste exemplo, o modelo da visualização [ViewModel01] herda as informações do modelo de ação [ActionModel03]. Isso ocorre com frequência. Assim, é possível utilizar um único modelo que servirá tanto como modelo de ação quanto como modelo de visualização. Criamos um novo modelo [ActionModel04]:
![]() |
que terá a seguinte estrutura:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_03.Models
{
[Bind(Exclude="Erreurs")]
public class ActionModel04
{
// ---------------------- Ação --------------------------------
[Required(ErrorMessage = "Le paramètre email est requis")]
[EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
public string Email { get; set; }
[Required(ErrorMessage = "Le paramètre jour est requis")]
[RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
public string Jour { get; set; }
[Required(ErrorMessage = "Le paramètre info1 est requis")]
[MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
public string Info1 { get; set; }
[Required(ErrorMessage = "Le paramètre info2 est requis")]
[MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
public string Info2 { get; set; }
[Required(ErrorMessage = "Le paramètre info3 est requis")]
[MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
[MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
public string Info3 { get; set; }
// ---------------------- visualização --------------------------------
public string Erreurs { get; set; }
}
}
- linhas 8-28: o modelo da ação com suas restrições de integridade. Esses campos também farão parte da visualização;
- linha 31: uma propriedade específica do modelo da visualização. Ela foi excluída do modelo da ação pela anotação da linha 5.
Criamos a seguinte nova ação [Action03]:
// Ação03
public ViewResult Action03(ActionModel04 modèle)
{
modèle.Erreurs = getErrorMessagesFor(ModelState);
return View(modèle);
}
- linha 2: [Action03] recebe o modelo de ação do tipo [ActionModel04];
- linha 5: e define como modelo de visualização esse mesmo modelo;
- linha 4: complementada com a informação [Erreurs];
Resta-nos apenas criar a visualização [/First/Action03.cshtml]:
![]() |
- em [1]: clique com o botão direito do mouse no código de [Action03] e, em seguida, em [Ajouter une vue];
- em [2]: o nome da visualização proposto por padrão;
- em [3]: indicar que se está criando uma visualização fortemente tipada;
- em [4]: selecione na lista suspensa a classe correta, neste caso a classe [ActionModel04];
- em [5]: a visualização criada.
Atribuímos à visualização [Action03] o mesmo código da visualização [Action02]. Apenas o modelo da visualização (linha 1) e o título da página (linha 11) mudam:
@model Exemple_03.Models.ActionModel04
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action03</title>
</head>
<body>
<h3>Informations du modèle de vue</h3>
<ul>
<li>Email : @Model.Email</li>
<li>Jour : @Model.Jour</li>
<li>Info1 : @Model.Info1</li>
<li>Info2 : @Model.Info2</li>
<li>Info3 : @Model.Info3</li>
<li>Erreurs : @Model.Erreurs</li>
</ul>
</body>
</html>
Agora, chamemos a ação [Action03] sem parâmetros:
![]() |
Os resultados são os mesmos de antes. É comum usar o mesmo modelo para a ação e para a visualização, pois o modelo da visualização frequentemente retoma informações do modelo da ação. Assim, utiliza-se um modelo mais abrangente, que pode ser usado tanto pela ação quanto pela visualização gerada por ela. É preciso ter o cuidado de excluir da ligação de dados as informações que não pertencem ao modelo da ação. Caso contrário, um usuário bem informado poderia inicializar partes do modelo da visualização sem o nosso conhecimento.
5.4. [Razor] – primeiros passos
Apresentaremos agora alguns elementos das visualizações [Razor], principalmente as instruções foreach e if.
Suponhamos que queiramos apresentar uma lista de pessoas em uma tabela HTML. O modelo da visualização poderia ser o seguinte [ViewModel02]:
![]() |
namespace Exemple_03.Models
{
public class ViewModel02
{
public Personne[] Personnes { get; set; }
public ViewModel02()
{
Personnes = new Personne[] { new Personne { Nom = "Pierre", Age = 44 }, new Personne { Nom = "Pauline", Age = 12 } };
}
}
public class Personne
{
public string Nom { get; set; }
public int Age { get; set; }
}
}
- a vista do modelo é a classe [ViewModel02], linhas 3-10;
- linha 5: o modelo possui um array de pessoas do tipo [Personne], definido nas linhas 12 a 16;
- linhas 6 a 10: o construtor do modelo inicializa a propriedade [Personnes] da linha 5 com uma matriz de duas pessoas.
A ação que gera esse modelo na saída será a seguinte: [Action04]:
// Ação04
public ViewResult Action04()
{
return View(new ViewModel02());
}
- linha 2: a ação não possui modelo de entrada;
- linha 4: ela passa para sua visualização padrão, uma instância do modelo [ViewModel02] que acabamos de definir.
A visualização [Action04.cshtml] exibirá o modelo [ViewModel02]:
![]() |
O código da visualização [Action04.cshtml] é o seguinte:
@model Exemple_03.Models.ViewModel02
@using Exemple_03.Models
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action04</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>Nom</th>
<th>Age</th>
</tr>
</thead>
<tbody>
@foreach (Personne p in Model.Personnes)
{
<tr>
<td>@p.Nom</td>
<td>@p.Age</td>
</tr>
}
</tbody>
</table>
</body>
</html>
- linha 1: o modelo da visualização;
- linha 2: a importação do espaço de nomes da classe [Personne] utilizada na linha 24;
- linhas 16-32: a tabela HTML que exibe as pessoas do modelo;
- linha 24: o início do código C# é sinalizado pelo caractere @. A instrução [foreach] irá percorrer todas as pessoas do modelo;
- linhas 26-27: o caractere < encerra o C# e inicia o HTML. Em seguida, novamente, o caractere @ para alternar para o C# e escrever o nome da pessoa. Depois, novamente o caractere <, que alterna para o modo HTML;
- linha 28: escreve-se a idade da pessoa.
A execução da ação [Action04] produz o seguinte resultado:
![]() |
Outros elementos de uma visualização podem ser preenchidos por uma coleção: listas, com ou sem menu suspenso, botões de opção e caixas de seleção. Consideremos o seguinte exemplo, que exibe uma lista suspensa.
O modelo [ViewModel05] será o seguinte:
![]() |
namespace Exemple_03.Models
{
public class ViewModel05
{
public Personne2[] Personnes { get; set; }
public int SelectedId { get; set; }
public ViewModel05()
{
Personnes = new Personne2[] {
new Personne2 { Id = 1, Prénom = "Pierre", Nom = "Martino" },
new Personne2 { Id = 2, Prénom = "Pauline", Nom = "Pereiro" },
new Personne2 { Id = 3, Prénom = "Jacques", Nom = "Alfonso" } };
SelectedId = 2;
}
}
public class Personne2
{
public int Id { get; set; }
public string Nom { get; set; }
public string Prénom { get; set; }
}
}
- linha 18: uma classe [Personne2] com três propriedades;
- linha 3: o modelo [ViewModel05] da visualização;
- linha 5: a lista de pessoas a serem exibidas na lista suspensa na forma [Prénom Nom];
- linha 6: o [Id] da pessoa a ser selecionada na lista suspensa;
- linhas 8-16: o construtor que cria uma tabela com três pessoas (linhas 10-13) e define o [Id] da pessoa que deve aparecer selecionada.
A visualização [Action05.cshtml] exibirá este modelo:
![]() |
Seu código é o seguinte:
@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action05</title>
</head>
<body>
<select>
@foreach (Personne2 p in Model.Personnes)
{
string selected = "";
if (p.Id == Model.SelectedId)
{
selected = "selected=\"selected\"";
}
<option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
}
</select>
</body>
</html>
As características da lista suspensa HTML foram apresentadas no parágrafo 2.5.2.6. Vamos relembrá-las:
Lista suspensa | <select size="1" name="cmbValeurs"> <option value="1">opção1</option> <option selected="selected" value="2">opção 2</option> <option value="3">opção 3</option> </select> |
tag HTML | <select size=".." name=".."> <option [selected="selected"] value=”v”>...</option> ... </select> exibe em uma lista os textos contidos entre as tags <option>...</option> |
atributos | name="cmbValeurs": nome do controle. size="1": número de itens visíveis na lista. size="1" transforma a lista no equivalente a uma caixa de combinação. selected="selected": se essa palavra-chave estiver presente para um elemento da lista, este aparecerá selecionado na lista. No nosso exemplo acima, o elemento da lista choix2 aparece como o elemento selecionado da caixa de combinação quando esta é exibida pela primeira vez. value=”v”: se o elemento for selecionado pelo usuário, é esse valor [v] que é enviado ao servidor. Na ausência desse atributo, é o texto exibido e selecionado que é enviado ao servidor. |
O código das linhas 17 a 25 gera as tags <option>, que são inseridas na tag <select> da linha 16.
- linha 17: percorre-se a lista de pessoas do modelo;
- linha 20: verifica-se se a pessoa atual é aquela que deve ser selecionada. Se for, prepara-se o texto selected="selected", que deve ser inserido na tag <option>;
- linha 24: a tag <option> é gravada.
Vamos solicitar a ação [Action05]:
![]() |
- em [1,2], as pessoas são exibidas na forma [Prénom Nom];
- No [1,2], a pessoa selecionada é aquela cujo [Id] é igual a 2.
Vamos agora examinar o código-fonte HTML da página acima:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action05</title>
</head>
<body>
<select>
<option value="1" >Pierre Martino</option>
<option value="2" selected="selected">Pauline Pereiro</option>
<option value="3" >Jacques Alfonso</option>
</select>
</body>
</html>
- linhas 10-12: as três tags <option> geradas pelo código [Razor];
- linha 11: foi mesmo a pessoa de [Id]=2 que foi selecionada.
Os dois exemplos acima são suficientes. Ao escrever uma visualização [Razor], é preciso resistir à tentação de incluir lógica nela. O código C# nos permitiria fazer isso. No entanto, no modelo MVC, a lógica deve estar na ação ou nas camadas inferiores [Metier, DAO], mas não na visualização. Mesmo respeitando o modelo MVC, pode-se acabar com muita lógica na visualização para calcular valores intermediários. Isso pode significar que o modelo utilizado não é detalhado o suficiente. Ele deve conter os valores finais de que a visualização precisa, para que ela não precise calculá-los por conta própria. Uma boa visualização é aquela em que há um mínimo de lógica e em que a estrutura HTML da visualização permanece clara. Se for inserido código C# em excesso, a estrutura HTML pode se tornar ilegível.
No exemplo acima, a lista suspensa poderia ser usada por um usuário e, nesse caso, gostaríamos de saber qual pessoa ele selecionou. Para isso, precisamos de um formulário.
5.5. Formulário – primeiros passos
O formulário apresentado ao usuário será o seguinte:
![]() |
O modelo da visualização será o modelo [ViewModel05], já utilizado anteriormente. A ação que exibirá essa visualização será a seguinte:
// Ação06-GET
[HttpGet]
public ViewResult Action06()
{
return View("Action06Get",new ViewModel05());
}
- linha 2: a ação só pode ser solicitada por um comando HTTP GET;
- linha 5: a visualização [/First/Action06Get.cshtml] será exibida com uma instância do tipo [ViewModel05] como modelo.
A visualização [/First/Action06Get.cshtml] será a seguinte:
![]() |
@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action06-GET</title>
</head>
<body>
<h3>Action06 - GET</h3>
<p>Choisissez une personne</p>
<form method="post" action="/First/Action06">
<select name="personneId">
@foreach (Personne2 p in Model.Personnes)
{
string selected = "";
if (p.Id == Model.SelectedId)
{
selected = "selected=\"selected\"";
}
<option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
}
</select>
<input name="valider" type="submit" value="Valider" />
</form>
</body>
</html>
As principais novidades são as seguintes:
- linha 18: para que o navegador possa transmitir as informações inseridas por um usuário, precisamos de um formulário. É a tag <form> nas linhas 18 e 31 que o delimita.
A tag HTML <form> foi apresentada no parágrafo 2.5.2.1. Vamos relembrar suas características:
formulário | |
tag HTML | <form name="..." method="..." action="...">...</form> |
atributos | name="frmexemple": nome do formulário — opcional method="...": método utilizado pelo navegador para enviar ao servidor web os valores coletados no formulário action="...": URL para onde serão enviados os valores coletados no formulário. Um formulário da Web é delimitado pelas tags <form>...</form>. O formulário pode ter um nome (name="xx"). Esse é o caso de todos os controles que podem ser encontrados em um formulário. O objetivo de um formulário é coletar informações fornecidas pelo usuário por meio do teclado/mouse e enviá-las para uma URL de servidor web. Qual? Aquela referenciada no atributo action="URL". Se esse atributo estiver ausente, as informações serão enviadas para o URL do documento no qual o formulário está localizado. Um cliente da Web pode utilizar dois métodos diferentes, chamados POST e GET, para enviar dados a um servidor web. O atributo method="méthode", com method igual a GET ou POST, da tag <form> indica ao navegador o método a ser utilizado para enviar as informações coletadas no formulário para o URL especificado pelo atributo action="URL". Quando o atributo method não é especificado, o método GET é utilizado por padrão. |
- linha 18: vemos que os valores do formulário serão enviados para o URL [/First/Action06] por meio de um comando HTTP POST;
- linha 30: um formulário deve ter um botão do tipo [submit]. É ele que aciona o envio dos valores inseridos para o URL, especificado pelo atributo [action] da tag <form>.
O que exatamente o navegador transmitirá quando o usuário clicar no botão [Valider]? Isso foi explicado no parágrafo 2.5.3.1. Vamos relembrar o que foi dito:
controle HTML | visual | valor(es) retornado(s) |
<input type="radio" value="Sim" name="R1"/>Sim <input type="radio" name="R1" value="não" checked="checked"/>Não | R1=Sim - o valor do atributo value do botão de opção marcado pelo usuário. | |
<input type="checkbox" name="C1" value="um"/>1 <input type="checkbox" name="C2" value="dois" checked="checked"/>2 <input type="checkbox" name="C3" value="três"/>3 | C1=um C2=dois - valores dos atributos value das caixas de seleção marcadas pelo usuário | |
<input type="text" name="txtSaisie" size="20" value="algumas palavras"/> | txtSaisida=programação+Web - texto digitado pelo usuário no campo de entrada. Os espaços foram substituídos pelo sinal + | |
<input type="password" name="txtMdp" size="20" value="unMotDePasse"/> | txtMdp=issoésegredo - texto digitado pelo usuário no campo de entrada | |
<textarea rows="2" name="areaSaisie" cols="20"> linha1 linha 2 linha3 </textarea> | areaSaisie=os+fundamentos+da%0D%0A programação+Web - texto digitado pelo usuário no campo de entrada. %OD%OA é o marcador de fim de linha. Os espaços foram substituídos pelo sinal + | |
<select size="1" name="cmbValeurs"> <option value='1'>opção1</option> <option selected="selected" value='2'>opção2</option> <option value='3'>opção3</option> </select> | cmbValores=3 - atributo [value] do elemento selecionado pelo usuário | |
<select size="3" name="lst1"> <option selected="selected" value='1'>lista1</option> <option value='2'>lista2</option> <option value='3'>lista3</option> <option value='4'>lista4</option> <option value='5'>lista5</option> </select> | ![]() | lst1=3 - atributo [value] do elemento selecionado pelo usuário |
<select size="3" name="lst2" multiple="multiple"> <option selected="selected" value='1'>lista1</option> <option value='2'>lista2</option> <option selected="selected" value='3'>lista3</option> <option value='4'>lista4</option> <option value='5'>lista5</option> </select> | lst2=1 lst2=3 - atributos [value] dos elementos selecionados pelo usuário | |
<input type="submit" value="Enviar" name="cmdRenvoyer"/> | cmdRenvoyer=Enviar - nome e atributo value do botão utilizado para enviar os dados do formulário ao servidor | |
<input type="hidden" name="secret" value="uneValeur"/> | secret=umValor - atributo value do campo oculto |
Em nosso formulário, temos duas tags capazes de enviar um valor:
<select name="personneId">
...
</select>
e
<input name="valider" type="submit" value="Valider" />
Se o usuário selecionar a pessoa nº 2, os valores serão enviados da seguinte forma:
Os nomes dos parâmetros correspondem aos atributos [name] das tags relacionadas ao POST. Sem esse atributo, as tags não enviam nenhum valor. Assim, no exemplo acima, poderíamos omitir o atributo name="valider" do botão [submit]. O valor enviado é o atributo [value] do botão. Nesse caso, essa informação não nos interessa. Às vezes, os formulários têm vários botões do tipo [submit]. Nesse caso, é importante saber qual botão foi clicado. Portanto, atribuiremos o atributo [name] aos diferentes botões.
A tag <select> é composta por uma sequência de tags <option>:
<select name="personneId">
<option value="1" >Pierre Martino</option>
<option value="2" selected="selected">Pauline Pereiro</option>
<option value="3" >Jacques Alfonso</option>
</select>
É o valor do atributo [value] da opção selecionada que é enviado. Na ausência desse atributo, é o texto exibido pela opção, por exemplo, [Pierre Martino], que é enviado.
A string
será publicada na próxima URL [/First/Action06]:
// Ação06-POST
[HttpPost]
public ViewResult Action06(ActionModel06 modèle)
{
return View("Action06Post",modèle);
}
Talvez nos lembremos de que já tínhamos uma ação [Action06]:
// Ação06-GET
[HttpGet]
public ViewResult Action06()
{
return View("Action06Get",new ViewModel05());
}
É possível ter duas ações com o mesmo nome, desde que elas não processem os mesmos comandos HTTP:
- [Action06] da linha 3 gerencia um POST (linha 2);
- [Action06] da linha c processa um GET (linha b).
A ação [Action06], que gerencia o POST, receberá a seguinte cadeia de parâmetros:
Precisamos de um modelo de ação para encapsular esses valores. Será o seguinte modelo [ActionModel06]:
![]() |
using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
public class ActionModel06
{
[Required(ErrorMessage = "Le paramètre [personneId] est requis")]
public int PersonneId { get; set; }
[Required(ErrorMessage = "Le paramètre [valider] est requis")]
public string Valider { get; set; }
}
}
A ação [Action06] recebe esse modelo e o transmite tal como está para a visualização [Action06Post] (linha 5 da ação), conforme segue:
![]() |
@model Exemple_03.Models.ActionModel06
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action06Post</title>
</head>
<body>
<h3>Action06 - POST</h3>
Valeurs postées :
<ul>
<li>ID de la personne sélectionnée : @Model.PersonneId</li>
<li>Commande utilisée : @Model.Valider</li>
</ul>
</body>
</html>
O modelo é exibido nas linhas 18 e 19.
Vejamos um exemplo:
![]() |
Em [1], seleciona-se a terceira pessoa de [Id], cujo valor é 3. Em [2], o formulário é enviado. Em [3], os valores recebidos. Em [4,5], percebe-se que o mesmo URL foi chamado, um por um GET [4], e a outra por um POST e um [5]. Isso não aparece no URL.
Na visualização exibida após o POST, talvez seja preferível exibir os nom e prénom da pessoa selecionada, em vez de seu número. É necessário, portanto, atualizar a visualização do POST e seu modelo.
Criamos uma ação [Action07] para lidar com esse caso. Essa ação precisará utilizar a sessão do usuário para armazenar nela a lista de pessoas. Seguiremos o modelo estudado no parágrafo 4.10, que permite incluir os dados de escopo [Application] e [Session] no modelo da ação.
O modelo da sessão será a seguinte classe [SessionModel]:
![]() |
namespace Exemple_03.Models
{
public class SessionModel
{
public Personne2[] Personnes { get; set; }
}
}
- linha 2: a sessão armazenará a lista de pessoas exibidas na lista suspensa;
Precisamos vincular o tipo anterior [SessionModel] a um binder que chamaremos de [SessionModelBinder]. Este será o mesmo descrito na página 82:
![]() |
using System.Web.Mvc;
namespace Exemple_03.Infrastructure
{
public class SessionModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// retornamos os dados do escopo [Session]
return controllerContext.HttpContext.Session["data"];
}
}
}
A ligação entre o modelo [SessionModel] e seus modelos binder e [SessionModelBinder] é feita no [Global.asax]:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
// model binders
ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
}
// Sessão
public void Session_Start()
{
Session["data"] = new SessionModel();
}
}
- linha 8: a ligação do modelo ao seu binder é feita no [Application_Start];
- linha 13: uma instância do tipo [SessionModel] é inserida na sessão associada à chave [data].
Feito isso, a ação [Action07] é a seguinte:
// Ação07-GET
[HttpGet]
public ViewResult Action07(SessionModel session)
{
ViewModel05 modèleVue = new ViewModel05();
session.Personnes= modèleVue.Personnes;
return View("Action07Get", modèleVue);
}
- linha 3: a ação recupera um tipo [SessionModel], ou seja, o dado de escopo [Session] associado à chave [data];
- linha 5: cria-se o modelo da visualização;
- linha 6: insere-se na sessão a tabela de pessoas. Ela será necessária na consulta seguinte, a do POST. O protocolo HTTP é um protocolo sem estado. É necessário utilizar uma sessão para manter a memória entre as consultas. Uma sessão é específica para um usuário e é gerenciada pelo servidor web;
- linha 7: a visualização [Action07Get.cshtml] é exibida. É a seguinte:
@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
...
<body>
<h3>Action07 - GET</h3>
<p>Choisissez une personne</p>
<form method="post" action="/First/Action07">
....
</form>
</body>
</html>
Ela é idêntica à visualização [Action06Get.cshtml] já analisada. A principal diferença está na linha 7: a URL, para a qual serão enviadas as entradas do formulário. Essas entradas serão processadas pela ação [Action07] a seguir:
// Ação07-POST
[HttpPost]
public ViewResult Action07(SessionModel session, ActionModel06 modèle)
{
Personne2 personne = session.Personnes.Where(p => p.Id == modèle.PersonneId).First<Personne2>();
string strPersonne = string.Format("{0} {1}", personne.Prénom, personne.Nom);
return View("Action07Post", (object)strPersonne);
}
- linha 3: os valores enviados são encapsulados no modelo de ação [ActionModel06], já utilizado anteriormente (abaixo):
using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
public class ActionModel06
{
[Required(ErrorMessage = "Le paramètre [personneId] est requis")]
public int PersonneId { get; set; }
[Required(ErrorMessage = "Le paramètre [valider] est requis")]
public string Valider { get; set; }
}
}
- linha 3: o primeiro parâmetro é o dado de escopo [Session] associado à chave [data];
- linha 5: uma consulta LINQ recupera a pessoa com o [Id] que foi publicado;
- linha 6: constrói-se a sequência de caracteres que deve ser exibida pela visualização [Action07Post] (linha 8);
- linha 7: para chamar o construtor correto [View], é necessário converter o tipo [string] para [object].
A visão [Action07Post.cshtml] é a seguinte:
@model string
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action07-Post</title>
</head>
<body>
<h3>Action07-POST</h3>
Vous avez sélectionné [@Model].
</body>
</html>
- linha 1: o modelo é do tipo [string];
- linha 16: a sequência de caracteres é exibida.
Veja um exemplo de execução:
![]() | ![]() |
5.6. Formulário – um exemplo completo
No parágrafo 2.5.2.1, analisamos o seguinte formulário HTML:
1 ![]() |
Vamos analisar uma ação [Action08Get] que exibe (GET) esse formulário e uma ação [Action08Post] que processa (POST) os valores inseridos pelo usuário. Um esquema clássico.
O modelo da visualização [1] acima será uma instância da classe [ViewModel08]. Essa classe será, ao mesmo tempo:
- o modelo da visualização gerada por um GET na ação [Action08Get];
- o modelo da ação [Action08Post] para uma solicitação POST.
![]() |
![]() |
5.6.1. O modelo de escopo [Application]
Vamos supor que os elementos exibidos pelos botões de opção, caixas de seleção e pelas diversas listas sejam dados do escopo [Application]. Um caso frequente. Essas informações provêm de um arquivo de configuração ou de um banco de dados processados na inicialização do aplicativo no método [Application_Start] de [Global.asax]. Esse método é executado da seguinte maneira:
protected void Application_Start()
{
....
// ligadores de modelo
ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
ModelBinders.Binders.Add(typeof(ApplicationModel), new ApplicationModelBinder());
// dados de escopo [Application]
Application["data"] = new ApplicationModel();
}
- linha 7: o tipo [ApplicationModel], que descreveremos em breve, está associado ao ligador de dados [ApplicationModelBinder], que já apresentamos na página 82;
- linha 10: uma instância do tipo [ApplicationModel] é registrada no dicionário da aplicação, associada à chave [data].
A classe [ApplicationModel] serve para encapsular todos os dados do escopo [Application]. Aqui, ela encapsulará os dados que o formulário deve exibir:
![]() |
namespace Exemple_03.Models
{
public class ApplicationModel
{
// as coleções a serem exibidas no formulário
public Item[] RadioButtonFieldItems { get; set; }
public Item[] CheckBoxesFieldItems { get; set; }
public Item[] DropDownListFieldItems { get; set; }
public Item[] SimpleChoiceListFieldItems { get; set; }
public Item[] MultipleChoiceListFieldItems { get; set; }
// inicialização de campos e coleções
public ApplicationModel()
{
RadioButtonFieldItems = new Item[]{
new Item {Value="1",Label="oui"},
new Item {Value="2", Label="non"}
};
CheckBoxesFieldItems = new Item[]{
new Item {Value="1",Label="1"},
new Item {Value="2", Label="2"},
new Item {Value="3", Label="3"}
};
DropDownListFieldItems = new Item[]{
new Item {Value="1",Label="choix1"},
new Item {Value="2", Label="choix2"},
new Item {Value="3", Label="choix3"}
};
SimpleChoiceListFieldItems = new Item[]{
new Item {Value="1",Label="liste1"},
new Item {Value="2", Label="liste2"},
new Item {Value="3", Label="liste3"},
new Item {Value="4", Label="liste4"},
new Item {Value="5", Label="liste5"}
};
MultipleChoiceListFieldItems = new Item[]{
new Item {Value="1",Label="liste1"},
new Item {Value="2", Label="liste2"},
new Item {Value="3", Label="liste3"},
new Item {Value="4", Label="liste4"},
new Item {Value="5", Label="liste5"}
};
}
// elemento das coleções
public class Item
{
public string Label { get; set; }
public string Value { get; set; }
}
}
}
- linhas 45-49: o elemento das diferentes coleções do formulário. [Label] é o texto exibido pelo elemento do formulário, [Value] é o valor enviado por esse elemento quando ele é selecionado;
- linha 6: a coleção exibida pelo botão de opção;
- linha 7: a coleção exibida pelas caixas de seleção;
- linha 8: a coleção exibida pela lista suspensa;
- linha 9: a coleção exibida pela lista de seleção única;
- linha 10: a coleção exibida pela lista de seleção múltipla;
- linhas 13-43: essas coleções são inicializadas pelo construtor sem parâmetros da classe.
As diferentes coleções irão preencher o seguinte formulário:
![]() |
5.6.2. O modelo da ação [Action08Get]
O formulário anterior será exibido pela seguinte ação [Action08Get]:
// Ação08-GET
[HttpGet]
public ViewResult Action08Get(ApplicationModel application)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View("Formulaire", new ViewModel08(application));
}
- linha 2: [Action08Get] responderá apenas a um comando [GET];
- linha 3: ela recebe como parâmetro o modelo do aplicativo que acabamos de descrever;
- linha 5: ela inicializa uma informação no contêiner dinâmico [ViewBag];
- linha 6: ela exibe a vista [/First/Formulaire.cshtml] com o modelo [ViewModel08]. Esse modelo será o do formulário apresentado anteriormente. Para isso, passamos ao construtor o modelo da aplicação que define os elementos a serem exibidos.
5.6.3. O modelo da visualização [Formulaire]
A classe [ViewModel08] será o modelo do formulário. Essa classe é a seguinte:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
namespace Exemple_03.Models
{
public class ViewModel08
{
// os campos de preenchimento
public string RadioButtonField { get; set; }
public string[] CheckBoxesField { get; set; }
public string TextField { get; set; }
public string PasswordField { get; set; }
public string TextAreaField { get; set; }
public string DropDownListField { get; set; }
public string SimpleChoiceListField { get; set; }
public string[] MultipleChoiceListField { get; set; }
// as coleções a serem exibidas no formulário
public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
// construtores
public ViewModel08()
{
}
public ViewModel08(ApplicationModel application)
{
// inicialização das coleções
RadioButtonFieldItems = application.RadioButtonFieldItems;
CheckBoxesFieldItems = application.CheckBoxesFieldItems;
DropDownListFieldItems = application.DropDownListFieldItems;
SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
// inicialização dos campos
RadioButtonField = "2";
CheckBoxesField = new string[] { "2" };
TextField = "quelques mots";
PasswordField = "secret";
TextAreaField = "ligne1\nligne2";
DropDownListField = "2";
SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };
}
}
}
- em um formulário, há dois tipos de elementos: aqueles que são exibidos e aqueles que são preenchidos;
- as linhas 20-24 definem os elementos a serem exibidos. São as diferentes coleções do formulário. Elas se encontram no modelo do aplicativo (linhas 34-38);
- linhas 10 a 17: definem os campos de preenchimento do formulário;
- linha 10: [RadioButtonField] recuperará o valor enviado pelas linhas seguintes do formulário:
<!-- os botões de opção -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="RadioButtonField" value="1" />oui
<input type="radio" name="RadioButtonField" value="2" checked="checked"/>non
</td>
</tr>
Observe-se, nas linhas 5 e 6, que o atributo [name] dos dois botões de opção é o nome da propriedade que será inicializada. Nos dados enviados, encontrará-se uma string no formato:
param1=val1&RadioButtonField=2¶m2=val2
se o usuário tiver marcado a opção denominada [non]. Na verdade, é o atributo [value] da opção marcada que é enviado.
- linha 11: [CheckBoxesField] recuperará os valores enviados pelas seguintes linhas do formulário:
<!-- as caixas de seleção -->
<tr>
<td>Cases à cocher</td>
<td>
<input type="checkbox" name="CheckBoxesField" value="1" />1
<input type="checkbox" name="CheckBoxesField" value="2" checked="checked"/>2
<input type="checkbox" name="CheckBoxesField" value="3" />3
</td>
Observe-se, nas linhas 5 e 6, que o atributo [name] das caixas de seleção é o nome da propriedade que será inicializada. Nos dados enviados, encontrar-se-á uma string no formato:
param1=val1&CheckBoxesField=2&CheckBoxesField=3¶m2=val2
se o usuário tiver marcado as caixas de seleção denominadas [2] e [3]. É o atributo [value] das opções marcadas que é enviado. Como vários parâmetros com o mesmo nome podem ser enviados, [CheckBoxesField] é uma matriz de valores e não um valor único. Se nenhuma opção estiver marcada, o parâmetro [CheckBoxesField] estará ausente da string enviada e a propriedade com o mesmo nome no modelo não será inicializada. Isso pode ser um problema, como veremos.
- linha 12: [TextField] recuperará o valor enviado pelas seguintes linhas do formulário:
<!-- campo de entrada de texto de linha única -->
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="TextField" value="quelques mots" size="30" />
</td>
</tr>
Na linha 5, o atributo [name] do campo de entrada é o nome da propriedade que será inicializada. Nos dados enviados, encontraremos uma string no formato:
param1=val1&TextField=abcdef¶m2=val2
se o usuário tiver digitado [abcdef] no campo de entrada.
- linha 13: [PasswordField] recuperará o valor inserido nas linhas seguintes do formulário:
<!-- o campo de entrada de senha -->
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="PasswordField" value="secret" size="30" />
</td>
</tr>
Na linha 5, o atributo [name] do campo de entrada é o nome da propriedade que será inicializada. Nos dados enviados, encontrará-se uma sequência no formato:
param1=val1&PasswordField=abcdef¶m2=val2
se o usuário tiver inserido [abcdef] no campo de entrada.
- linha 14: [TextAreaField] recuperará o valor enviado pelas seguintes linhas do formulário:
<!-- o campo de entrada de texto de várias linhas -->
<tr>
<td>Boîte de saisie</td>
<td>
<textarea name="TextAreaField" cols="40" rows="3">ligne1
ligne2</textarea>
</td>
</tr>
Na linha 5, o atributo [name] do campo de entrada é o nome da propriedade que será inicializada. Nos dados enviados, encontrará-se uma sequência no formato:
param1=val1&TextAreaField=abcdef%0D%OAhijk¶m2=val2
se o usuário tiver digitado [abcdef] seguido de um salto de linha e de [ijk] no campo de entrada.
- linha 15: [DropDownListField] recuperará o valor enviado pelas linhas seguintes do formulário:
<!-- a lista suspensa -->
<tr>
<td>Liste déroulante</td>
<td>
<select name="DropDownListField">
<option value="1" >choix1</option>
<option value="2" selected="selected">choix2</option>
<option value="3" >choix3</option>
</select>
</tr>
Na linha 5, o atributo [name] da tag <select> é o nome da propriedade que será inicializada. Nos dados enviados, encontraremos uma sequência no formato:
param1=val1&DropDownListField=1¶m2=val2
se o usuário tiver selecionado a opção [choix1]. É o atributo [value] da opção selecionada que é enviado.
- linha 16: [SingleChoiceListField] recuperará o valor enviado pelas seguintes linhas do formulário:
<!-- a lista de seleção única -->
<tr>
<td>Liste à choix unique</td>
<td>
<select name="SimpleChoiceListField" size="3">
<option value="1" >liste1</option>
<option value="2" >liste2</option>
<option value="3" selected="selected">liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
</select>
</tr>
Na linha 5, o atributo [name] da tag <select> é o nome da propriedade que será inicializada. É o atributo [size="3"] que faz com que não haja uma lista suspensa. Nos dados enviados, encontraremos uma string no formato:
param1=val1&SimpleChoiceListField=3¶m2=val2
se o usuário tiver selecionado a opção [liste3]. É o atributo [value] da opção selecionada que é enviado. O parâmetro [SingleChoiceListField] pode estar ausente da string enviada se nenhum elemento tiver sido selecionado.
- linha 17: [MultipleChoiceListField] recuperará os valores enviados pelas seguintes linhas do formulário:
<!-- a lista de múltipla escolha -->
<tr>
<td>Liste à choix multiple</td>
<td>
<select name="MultipleChoiceListField" size="3" multiple="multiple">
<option value="1" selected="selected">liste1</option>
<option value="2" >liste2</option>
<option value="3" selected="selected">liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
</select>
</tr>
Na linha 5, o atributo [name] da tag <select> é o nome da propriedade que será inicializada. É o atributo [size="3"] que faz com que não haja uma lista suspensa, e o atributo [multiple] que permite que o usuário selecione vários elementos mantendo pressionada a tecla [Ctrl]. Nos dados enviados, encontraremos uma string no formato:
param1=val1&MultipleChoiceListField=1&MultipleChoiceListField=3¶m2=val2
se o usuário tiver selecionado as opções [liste1] e [liste3]. É o atributo [value] das opções selecionadas que é enviado. Como vários parâmetros com o mesmo nome podem ser enviados, [MultipleChoiceListField] é uma matriz de valores e não um valor simples. Se nenhuma opção estiver marcada, o parâmetro [MultipleChoiceListField] estará ausente da sequência enviada e a propriedade com o mesmo nome no modelo não será inicializada.
Os diversos campos de entrada apresentados anteriormente receberão os valores enviados pelo formulário. Também é possível inicializá-los antes de enviar o formulário. Foi isso que foi feito aqui:
// inicialização de campos
RadioButtonField = "2";
CheckBoxesField = new string[] { "2" };
TextField = "quelques mots";
PasswordField = "secret";
TextAreaField = "ligne1\nligne2";
DropDownListField = "2";
SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };
Se esses valores tivessem sido obtidos após o envio do formulário com o código POST, isso significaria que o usuário:
- linha 2: marcado a opção [non] do botão de opção;
- linha 3: marcado a opção [2] nas caixas de seleção;
- linha 4: digitou [quelques mots] no campo de entrada;
- linha 5: digitou [secret] como senha;
- linha 6: digitou [ligne1\nligne2] no campo de entrada multilinha;
- linha 7: selecione a opção [choix2] na lista suspensa;
- linha 8: selecionei a opção [liste3] da lista de escolha única;
- linha 9: selecionei as opções [liste1] e [liste3] da lista de seleção múltipla;
Vamos supor que uma ação POST tenha ocorrido e que desejemos reenviar o formulário exatamente como foi preenchido. Isso é o que ocorre, por exemplo, quando se reenvia ao usuário um formulário com erros. O formulário é reenviado exatamente como foi preenchido.
5.6.4. A visualização [Formulaire]
A visualização [/First/Formulaire.cshtml] exibe o formulário:
![]() |
@model Exemple_03.Models.ViewModel08
@using Exemple_03.Models
@{
Layout = null;
}
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Formulaire</title>
</head>
<body>
<form method="post" action="Action08Post">
<h2>Formulaire ASP.NET MVC</h2>
<h3>Affiché par : @ViewBag.info</h3>
<table>
<thead></thead>
<tbody>
<!-- botões de opção -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
@foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
{
string strChecked = item.Value == @Model.RadioButtonField ? "checked=\"checked\"" : "";
<input type="radio" name="RadioButtonField" value="@item.Value" @strChecked/>@item.Label
<text/>
}
</td>
</tr>
...
</tbody>
</table>
<input type="submit" value="Valider" />
</form>
</body>
</html>
- linha 1: [ViewModel08] é o modelo do formulário;
- linha 12: a tag <form> do formulário. Este será enviado pelo método [POST] (atributo method) para o URL [/First/Action08Post] (atributo action);
- linha 33: o botão do tipo [submit], que serve para enviar o formulário;
- linhas 22-27: exibem os botões de opção:
- linha 22: percorre-se a coleção exibida pelo botão de opção;
- linha 24: o botão cujo atributo [value] tem o valor da propriedade [RadioButtonField] deve estar marcado. Para isso, ele deve ter o atributo [checked="checked"];
- linha 25: geração da tag <input type="radio"> com o valor [@item.Value] e o texto [@item.Label];
- linha 26: a tag <text/> não é uma tag HTML reconhecida. Ela está presente para [Razor]. Ao encontrá-la, [Razor] irá gerar um salto de linha. Isso não afeta o formulário exibido, mas afeta o código HTML gerado. As tags <input type="radio"> ficam, então, em duas linhas diferentes, em vez de ficarem na mesma linha. Isso torna o código mais legível quando, no navegador, solicitamos a visualização do código-fonte da página exibida;
Vamos examinar os demais elementos da visualização:
<!-- caixas de seleção -->
<tr>
<td>Cases à cocher</td>
<td>
@{
foreach (ApplicationModel.Item item in @Model.CheckBoxesFieldItems)
{
string strChecked = @Model.CheckBoxesField.Contains(item.Value) ? "checked=\"checked\"" : "";
<input type="checkbox" name="CheckBoxesField" value="@item.Value" @strChecked/>@item.Label
<text/>
}
}
</td>
- linha 6: percorremos a coleção exibida pelas caixas de seleção;
- linha 8: uma célula que tenha como atributo [value] um dos valores da propriedade [CheckBoxesField] deve estar marcada. Para isso, ela deve ter o atributo [checked="checked"]. Utiliza-se uma expressão LINQ que permite verificar se um valor está contido em uma tabela;
- linha 25: geração da tag <input type="checkbox"> com o valor [@item.Value] e o texto [@item.Label];
<!-- o campo de entrada de texto de linha única -->
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="TextField" value="@Model.TextField" size="30" />
</td>
</tr>
<!-- o campo de digitação de senha -->
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="PasswordField" value="@Model.PasswordField" size="30" />
</td>
</tr>
<!-- o campo de entrada de texto de várias linhas -->
<tr>
<td>Boîte de saisie</td>
<td>
<textarea name="TextAreaField" cols="40" rows="3">@Model.TextAreaField</textarea>
</td>
</tr>
- linhas 5, 12: atribui-se ao atributo [value] da tag o valor do modelo;
- linha 19: o mesmo, mas com uma sintaxe diferente.
<!-- a lista suspensa -->
<tr>
<td>Liste déroulante</td>
<td>
<select name="DropDownListField">
@{
foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
{
string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
- linha 7: percorre-se a coleção exibida pela lista suspensa;
- linha 9: deve ser selecionada uma opção cujo atributo [value] tenha o valor da propriedade [DropDownListField]. Para isso, ela deve ter o atributo [selected="selected"];
- linha 25: geração da tag <option value="valeur">libellé</option> com o valor [@item.Value] e o texto [@item.Label];
<!-- a lista de seleção única -->
<tr>
<td>Liste à choix unique</td>
<td>
<select name="SimpleChoiceListField" size="3">
@{
foreach (ApplicationModel.Item item in @Model.SimpleChoiceListFieldItems)
{
string strChecked = item.Value == @Model.SimpleChoiceListField ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
A explicação é a mesma que para a lista suspensa.
<!-- a lista de seleção múltipla -->
<tr>
<td>Liste à choix multiple</td>
<td>
<select name="MultipleChoiceListField" size="3" multiple="multiple">
@{
foreach (ApplicationModel.Item item in @Model.MultipleChoiceListFieldItems)
{
string strChecked = @Model.MultipleChoiceListField.Contains(item.Value) ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
- linha 7: percorre-se a coleção exibida pela lista;
- linha 9: deve ser selecionada uma opção cujo atributo [value] tenha um dos valores da propriedade [MultipleChoiceListField]. Para isso, ela deve ter o atributo [selected="selected"]. Utiliza-se uma expressão LINQ que permite verificar se um valor está contido em uma matriz;
- linha 10: geração da tag libellé/option com o valor [@item.Value] e o rótulo [@item.Label];
5.6.5. Processamento do POST do formulário
Vimos que o formulário seria enviado para a ação [Action08Post]:
<form method="post" action="Action08Post">
A ação [Action08Post] é a seguinte:
// Ação08-POST
[HttpPost]
public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
ViewModel08 modèle = new ViewModel08(application);
TryUpdateModel(modèle,posted);
return View("Formulaire", modèle);
}
- linha 3: o modelo da aplicação é passado como parâmetro, assim como os valores enviados. Estes estão disponíveis em um tipo [FormCollection]. O valor do parâmetro [RadioButtonField] enviado é obtido pela expressão posted[" RadioButtonField"]. O resultado é uma cadeia de caracteres ou o ponteiro null. Se escrevermos posted[" CheckBoxesField"], teremos uma matriz de cadeias de caracteres ou o ponteiro null;
- por que não escrever:
public ViewResult Action08Post(ApplicationModel application, ViewModel08 posted)
Há duas razões:
- a primeira é que o framework instanciará o modelo [ViewModel08] com o construtor sem parâmetros, o que fará com que as coleções do modelo não sejam inicializadas;
- a segunda é que queremos controlar o que vai para o modelo. Sabemos que há quatro fontes possíveis para o modelo: os parâmetros de um GET, de um POST, da rota utilizada e os de um arquivo uploadé. Aqui, queremos inicializar o modelo apenas com os valores inseridos.
- linha 6: instanciamos o modelo usando o construtor correto;
- linha 7: inicializamos o modelo com os valores enviados. Após essa operação, o modelo corresponde aos dados inseridos pelo usuário;
- linha 8: exibimos o formulário novamente. O usuário o encontrará exatamente como foi preenchido.
Vejamos um exemplo:
![]() |
No [2], o resultado do [POST] reflete corretamente o que foi inserido no [1].
5.6.6. Tratamento de anomalias no POST
Mencionamos que, se nenhum valor fosse marcado ou selecionado nos campos do [CheckBoxesField, SimpleChoiceListField, MultipleChoiceListField], os parâmetros correspondentes não fariam parte da string enviada e, portanto, as propriedades com os mesmos nomes no modelo não seriam inicializadas.
Vejamos o seguinte exemplo:
![]() |
- no [1], nenhuma caixa de seleção foi marcada;
- em [2], o [POST] retorna uma caixa marcada.
A explicação é a seguinte:
- como não há nenhuma caixa marcada, o parâmetro [CheckBoxesField] não faz parte dos valores enviados;
- a ação [Action08Post] procede da seguinte maneira:
[HttpPost]
public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = ...
ViewModel08 modèle = new ViewModel08(application);
TryUpdateModel(modèle,posted);
return View("Formulaire", modèle);
}
- linha 5: o modelo do formulário é instanciado. No entanto, o construtor utilizado atribui a matriz ["2"] à propriedade [CheckBoxesField];
- linha 6: os valores lançados são registrados no modelo. Como o parâmetro [CheckBoxesField] não faz parte dos valores postados, a propriedade com o mesmo nome não é atribuída. Ela permanece, portanto, com seu valor ["2"], o que faz com que, na exibição, a caixa nº 2 esteja marcada, embora não devesse estar.
Esse problema pode ser resolvido de várias maneiras. Optamos por resolvê-lo no código da ação [Action08Post]:
// Ação08-POST
[HttpPost]
public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
ViewModel08 modèle = new ViewModel08(application);
TryUpdateModel(modèle,posted);
// processamento de valores não lançados
if (posted["CheckBoxesField"] == null)
{
modèle.CheckBoxesField = new string[] { };
}
if (posted["SimpleChoiceListField"] == null)
{
modèle.SimpleChoiceListField = "";
}
if (posted["MultipleChoiceListField"] == null)
{
modèle.MultipleChoiceListField = new string[] { };
}
// exibição do formulário
return View("Formulaire", modèle);
}
- linhas 9-20: verifica-se se determinados parâmetros foram ou não enviados. Caso contrário, eles são inicializados com o valor que corresponde à ausência de entrada feita pelo usuário. O teste não foi realizado para a lista suspensa, que sempre tem um item selecionado, o que não ocorre nas outras listas.
O leitor é convidado a testar esta nova versão.
5.7. Uso de métodos especializados na geração de formulários
5.7.1. O novo formulário
Criamos um novo formulário [Formulaire2.cshtml] que gerará um formulário idêntico ao anterior:
![]() |
Vamos revisar o código usado para gerar a lista suspensa do formulário:
<!-- a lista suspensa -->
<tr>
<td>Liste déroulante</td>
<td>
<select name="DropDownListField">
@{
foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
{
string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
Esse código apresenta duas desvantagens:
- o mais importante é que se perde de vista a natureza do componente — neste caso, uma lista suspensa — devido à complexidade do código;
- linha 5: se houver um erro no nome da propriedade do modelo a ser usada como atributo [name], só perceberemos isso na hora da execução.
ASP.NET MVC oferece métodos especializados chamados [HTML Helpers] que, como o próprio nome indica, visam facilitar a geração do HTML, especialmente para formulários. Com essas classes, a lista suspensa anterior é escrita da seguinte forma:
<!-- a lista suspensa -->
<tr>
<td>Liste déroulante</td>
<td>@Html.DropDownListFor(m => m.DropDownListField,
new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
</td>
</tr>
A lista suspensa é gerada pelas linhas 4 e 5. O código é significativamente menos complexo. O código HTML gerado para a lista suspensa é o seguinte:
<!-- a lista suspensa -->
<tr>
<td>Liste déroulante</td>
<td><select id="DropDownListField" name="DropDownListField"><option value="1">choix1</option>
<option selected="selected" value="2">choix2</option>
<option value="3">choix3</option>
</select></td>
</tr>
- linha 4: o atributo [name] está correto;
- linhas 4-6: as opções foram geradas corretamente e a opção correta foi selecionada.
Voltemos ao código que gerou essas linhas HTML:
@Html.DropDownListFor(m => m.DropDownListField, new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
- o primeiro parâmetro é uma função lambda (esse é o nome dela), em que m representa o modelo da visualização e m.DropDowListField é uma propriedade desse modelo. O gerador de código HTML utilizará o nome dessa propriedade para gerar os atributos [id] e [name] do [select] que será gerado. Se for utilizada uma propriedade inexistente, ocorrerá um erro na compilação e não mais na execução. Trata-se de uma melhoria em relação à solução anterior, na qual os erros de nomenclatura eram detectados apenas na execução;
- o segundo parâmetro serve para designar a coleção de elementos que alimentará a lista suspensa. A classe [SelectList] permite construir essa coleção:
- seu primeiro parâmetro é uma coleção qualquer de elementos. Aqui, temos uma coleção do tipo [Item];
- seu segundo parâmetro é a propriedade dos elementos que fornecerá o valor da tag <option>. Aqui, trata-se da propriedade [Value] da classe [Item];
- seu terceiro parâmetro é a propriedade dos elementos que fornecerá o texto da tag <option>. Aqui, trata-se da propriedade [Label] da classe [Item];
- para saber qual opção deve ser selecionada (atributo selected), o framework faz o mesmo que nós: compara o valor da opção com o valor atual da propriedade [DropDownListField].
Vamos ver agora os outros métodos que podemos usar:
Botões de opção
O novo código é o seguinte:
<!-- botões de opção -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
@{
foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
{
@Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)@item.Label
<text/>
}
}
</td>
</tr>
O código HTML gerado é o seguinte:
<!-- botões de opção -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input id="RadioButtonField" name="RadioButtonField" type="radio" value="1" />oui
<input checked="checked" id="RadioButtonField" name="RadioButtonField" type="radio" value="2" />non
</td>
</tr>
O método utilizado é [Html.RadioButtonFor]:
- o primeiro parâmetro é a propriedade do modelo que será associada ao botão de opção (atributo [name]);
- o segundo parâmetro é o valor a ser atribuído ao botão de opção (atributo [value]).
Caixas de seleção
O código é alterado da seguinte forma:
<!-- as caixas de seleção -->
<tr>
<td>Cases à cocher</td>
<td>
@{
@Html.CheckBoxFor(m=>m.CheckBoxField1) @Model.CheckBoxesFieldItems[0].Label
@Html.CheckBoxFor(m=>m.CheckBoxField2) @Model.CheckBoxesFieldItems[1].Label
@Html.CheckBoxFor(m=>m.CheckBoxField3) @Model.CheckBoxesFieldItems[2].Label
}
</td>
O método utilizado para gerar caixas de seleção é [Html.CheckBoxFor]:
O parâmetro é a propriedade booleana do modelo que será associada à caixa de seleção. Se for [Propriété=true], a caixa estará marcada. Se for [Propriété=false], a caixa não estará marcada. Em todos os casos, o atributo [value] tem o valor true. O código HTML gerado é o seguinte:
<input id="Propriété" name="Propriété" type="checkbox" value="true" />
<input name="Propriété" type="hidden" value="false" />
- linha 1: a caixa de seleção com o atributo [value="true"];
- linha 2: um campo oculto (type=hidden) com o mesmo nome [Propriété] que a caixa de seleção com o atributo [value="false"]. Por que duas tags [input] com o mesmo nome? Há dois casos:
- a caixa de seleção da linha 1 está marcada. Nesse caso, a sequência de parâmetros enviada é Propriedade=true&Propriedade=false (linhas 1 e 2). Como a propriedade [Propriété] espera apenas um valor, pode-se supor que o framework atribua o valor [true] a [Propriété]. Bastaria realizar uma operação lógica OU entre os valores recebidos para chegar a esse resultado;
- a caixa de seleção da linha 1 não está marcada. Portanto, a sequência de parâmetros enviada é Propriedade=false (apenas na linha 2) e, assim, a propriedade [Propriété] recebe o valor [false], o que está correto (a caixa de seleção não foi marcada).
Campo de entrada de linha única
O novo código é o seguinte:
<!-- o campo de entrada de texto de linha única -->
<tr>
<td>Champ de saisie</td>
<td>
@Html.TextBoxFor(m => m.TextField, new { size = "30" })
</td>
</tr>
O código HTML gerado é o seguinte:
<!-- o campo de entrada de texto de uma linha -->
<tr>
<td>Champ de saisie</td>
<td>
<input id="TextField" name="TextField" size="30" type="text" value="quelques mots" />
</td>
</tr>
O método utilizado é o seguinte:
@Html.TextBoxFor(m => m.TextField, new { size = "30" })
- o primeiro parâmetro especifica a propriedade do modelo associado ao campo de entrada. O nome da propriedade será utilizado nos atributos [name] e [id] da tag <input> gerada, e seu valor será atribuído ao atributo [value];
- o segundo parâmetro é uma classe anônima que especifica determinados atributos da tag HTML gerada, neste caso, o atributo [size].
Campo de digitação de senha
O novo código é o seguinte:
<!-- o campo de digitação de senha -->
<tr>
<td>Mot de passe</td>
<td>
@Html.PasswordFor(m => m.PasswordField, new { size = "15" })
</td>
</tr>
O código HTML gerado é o seguinte:
<!-- o campo de entrada de senha -->
<tr>
<td>Mot de passe</td>
<td>
<input id="PasswordField" name="PasswordField" size="15" type="password" />
</td>
</tr>
O método utilizado é o seguinte:
@Html.PasswordFor(m => m.PasswordField, new { size = "15" })
O funcionamento é semelhante ao do método [Html.TexBoxFor].
Campo de entrada com várias linhas
O novo código é o seguinte:
<!-- o campo de entrada de texto de várias linhas -->
<tr>
<td>Boîte de saisie</td>
<td>
@Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
</td>
</tr>
O código HTML gerado é o seguinte:
<!-- o campo de entrada de texto de várias linhas -->
<tr>
<td>Boîte de saisie</td>
<td>
<textarea cols="30" id="TextAreaField" name="TextAreaField" rows="5">
ligne1
ligne2</textarea>
</td>
</tr>
O método utilizado é o seguinte:
@Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
O funcionamento é semelhante ao do método [Html.TexBoxFor].
Lista de escolha única
O novo código é o seguinte:
<!-- a lista de seleção única -->
<tr>
<td>Liste à choix unique</td>
<td>
@Html.DropDownListFor(m => m.SimpleChoiceListField, new SelectList(@Model.SimpleChoiceListFieldItems, "Value", "Label"), new { size = "3" })
</tr>
e o código HTML gerado é o seguinte:
<!-- a lista de seleção única -->
<tr>
<td>Liste à choix unique</td>
<td>
<select id="SimpleChoiceListField" name="SimpleChoiceListField" size="3">
<option value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>
Já analisamos o método [Html.DropDownListFor]. A única diferença aqui é o terceiro parâmetro, que serve para especificar um atributo [size] diferente de 1. É essa característica que faz a transição de uma lista suspensa [size=1] para uma lista simples.
A lista de múltiplas opções
O novo código é o seguinte:
<!-- a lista de seleção múltipla -->
<tr>
<td>Liste à choix multiple</td>
<td>
@Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
</tr>
e o código HTML gerado é o seguinte:
<!-- a lista de seleção múltipla -->
<tr>
<td>Liste à choix multiple</td>
<td>
<select id="MultipleChoiceListField" multiple="multiple" name="MultipleChoiceListField" size="5">
<option selected="selected" value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>
O método
@Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
funciona como o método [Html.DropDownListFor], com a diferença de que gera uma lista de seleção múltipla. As opções selecionadas são aquelas cujo valor (atributo value) consta na tabela [MultipleChoiceListField].
A tag <form> também pode ser gerada por meio de um método:
@using (Html.BeginForm("Action09Post", "First"))
{
...
}
O código HTML gerado é o seguinte:
<form action="/First/Action09Post" method="post">
...
</form>
O método
Html.BeginForm("Action09Post", "First")
tem como primeiro parâmetro o nome de uma ação e, como segundo parâmetro, o nome de um controlador.
5.7.2. As ações e o modelo
O formulário será gerado pela seguinte ação [Action09Get]:
// Ação09-GET
[HttpGet]
public ViewResult Action09Get(ApplicationModel application)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View("Formulaire2", new ViewModel09(application));
}
A visualização gerada na linha 6 é [Formulaire2], associada ao seguinte modelo [ViewModel09]:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
namespace Exemple_03.Models
{
public class ViewModel09
{
// os campos de preenchimento
public string RadioButtonField { get; set; }
public bool CheckBoxField1 { get; set; }
public bool CheckBoxField2 { get; set; }
public bool CheckBoxField3 { get; set; }
public string TextField { get; set; }
public string PasswordField { get; set; }
public string TextAreaField { get; set; }
public string DropDownListField { get; set; }
public string SimpleChoiceListField { get; set; }
public string[] MultipleChoiceListField { get; set; }
// as coleções a serem exibidas no formulário
public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
// construtores
public ViewModel09()
{
}
public ViewModel09(ApplicationModel application)
{
// inicialização de coleções
RadioButtonFieldItems = application.RadioButtonFieldItems;
CheckBoxesFieldItems = application.CheckBoxesFieldItems;
DropDownListFieldItems = application.DropDownListFieldItems;
SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
// inicialização dos campos
RadioButtonField = "2";
CheckBoxField2 = true;
TextField = "quelques mots";
PasswordField = "secret";
TextAreaField = "ligne1\nligne2";
DropDownListField = "2";
SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };
}
}
}
[ViewModel09] difere de [ViewModel08] no gerenciamento das caixas de seleção. Em vez de uma tabela com três caixas de seleção, foram utilizadas três caixas de seleção separadas (linhas 11 a 13).
O formulário será processado pela ação [Action09Post] a seguir:
// Ação09-POST
[HttpPost]
public ViewResult Action09Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
ViewModel09 modèle = new ViewModel09(application);
TryUpdateModel(modèle, posted);
// processamento de valores não enviados
if (posted["SimpleChoiceListField"] == null)
{
modèle.SimpleChoiceListField = "";
}
if (posted["MultipleChoiceListField"] == null)
{
modèle.MultipleChoiceListField = new string[] { };
}
// exibição do formulário
return View("Formulaire2", modèle);
}
A ação [Action09Post] é idêntica à ação [Action08Post], exceto em dois pontos:
- linha 18: a visualização [Formulaire2] é utilizada em vez da visualização [Formulaire];
- não há mais o gerenciamento das caixas de seleção que foram desmarcadas. Isso agora é gerenciado corretamente pelo método [Html.CheckBoxFor].
5.8. Geração de um formulário a partir dos metadados do modelo
Existem outros métodos, além dos anteriores, para gerar um formulário. Um deles consiste em associar informações a um campo do modelo, o que permitirá que o framework MVC saiba qual tag de entrada deve gerar. Essas informações são chamadas de metadados.
Consideremos o seguinte modelo de visualização [ViewModel10]:
![]() |
using System;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
namespace Exemple_03.Models
{
public class ViewModel10
{
[Display(Name="Text")]
[DataType(DataType.Text)]
public string Text { get; set; }
[Display(Name = "TextArea")]
[DataType(DataType.MultilineText)]
public string MultiLineText { get; set; }
[Display(Name = "Number")]
public int Number { get; set; }
[Display(Name = "Decimal")]
[UIHint("Decimal")]
public double Decimal { get; set; }
[Display(Name = "Tel")]
[DataType(DataType.PhoneNumber)]
public string Tel { get; set; }
[Display(Name = "Date")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
[Display(Name = "Time")]
[DataType(DataType.Time)]
public DateTime Time { get; set; }
[Display(Name = "HiddenInput")]
[UIHint("HiddenInput")]
public string HiddenInput { get; set; }
[Display(Name = "Boolean")]
[UIHint("Boolean")]
public bool Boolean { get; set; }
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
public string Email{ get; set; }
[Display(Name = "Url")]
[DataType(DataType.Url)]
public string Url { get; set; }
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Currency")]
[DataType(DataType.Currency)]
public double Currency { get; set; }
[Display(Name = "CreditCard")]
[DataType(DataType.CreditCard)]
public string CreditCard { get; set; }
// construtor
public ViewModel10()
{
Text = "tra la la";
MultiLineText = "ligne1\nligne2";
Number = 4;
Decimal = 10.2;
Tel = "0617181920";
Date = DateTime.Now;
Time = DateTime.Now;
HiddenInput = "caché";
Boolean = true;
Email = "x@y.z";
Url = "http://istia.univ-angers.fr";
Password = "mdp";
Currency = 4.2;
CreditCard = "0123456789012345";
}
}
}
Os metadados são formados pelas tags [Display, DataType, UIHint].
Esse modelo de visualização será construído pela seguinte ação [Action10Get]:
// Ação10-GET
[HttpGet]
public ViewResult Action10Get()
{
return View(new ViewModel10());
}
Na linha 5 acima, solicita-se à visualização padrão da ação [/First/Action10Get.cshtml ] que exiba o modelo de visualização do tipo [ViewModel10]. Essa visualização é a seguinte:
![]() |
@model Exemple_03.Models.ViewModel10
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action10Get</title>
</head>
<body>
<h3>Formulaire ASP.NET MVC - 2</h3>
@using (Html.BeginForm("Action10Post", "First"))
{
<table>
<thead>
<tr>
<th>LabelFor</th>
<th>EditorFor</th>
<th>DisplayFor</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Html.LabelFor(m => m.Text)</td>
<td>@Html.EditorFor(m => m.Text)</td>
<td>@Html.DisplayFor(m => m.Text)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.MultiLineText)</td>
<td>@Html.EditorFor(m => m.MultiLineText)</td>
<td>@Html.DisplayFor(m => m.MultiLineText)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Number)</td>
<td>@Html.EditorFor(m => m.Number)</td>
<td>@Html.DisplayFor(m => m.Number)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Decimal)</td>
<td>@Html.EditorFor(m => m.Decimal)</td>
<td>@Html.DisplayFor(m => m.Decimal)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Tel)</td>
<td>@Html.EditorFor(m => m.Tel)</td>
<td>@Html.DisplayFor(m => m.Tel)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Date)</td>
<td>@Html.EditorFor(m => m.Date)</td>
<td>@Html.DisplayFor(m => m.Date)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Time)</td>
<td>@Html.EditorFor(m => m.Time)</td>
<td>@Html.DisplayFor(m => m.Time)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.HiddenInput)</td>
<td>@Html.EditorFor(m => m.HiddenInput)</td>
<td>@Html.DisplayFor(m => m.HiddenInput)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Boolean)</td>
<td>@Html.EditorFor(m => m.Boolean)</td>
<td>@Html.DisplayFor(m => m.Boolean)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Email)</td>
<td>@Html.EditorFor(m => m.Email)</td>
<td>@Html.DisplayFor(m => m.Email)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Url)</td>
<td>@Html.EditorFor(m => m.Url)</td>
<td>@Html.DisplayFor(m => m.Url)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Password)</td>
<td>@Html.EditorFor(m => m.Password)</td>
<td>@Html.DisplayFor(m => m.Password)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Currency)</td>
<td>@Html.EditorFor(m => m.Currency)</td>
<td>@Html.DisplayFor(m => m.Currency)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.CreditCard)</td>
<td>@Html.EditorFor(m => m.CreditCard)</td>
<td>@Html.DisplayFor(m => m.CreditCard)</td>
</tr>
</tbody>
</table>
<input type="submit" value="Valider" />
}
</body>
</html>
Para cada uma das propriedades do modelo, utilizamos o método:
- Html.LabelFor para exibir o valor do metadado [DisplayName] da propriedade;
- Html.EditorFor para gerar a tag HTML de inserção do valor da propriedade. Esse método utilizará os metadados [DataType] e [UIHint] da propriedade;
- Html.DisplayFor para exibir o valor da propriedade de acordo com o formato indicado pelo metadado [DataType].
Veja um exemplo de execução no navegador Chrome:

Dependendo do navegador utilizado, podem ser exibidas páginas diferentes. De fato, a visualização gerada utiliza as novas tags introduzidas pela versão 5 do HTML, denominada HTML5. Nem todos os navegadores suportam essa versão ainda. No exemplo acima, o navegador Chrome a suporta parcialmente.
5.8.1. O [POST] do formulário
O [POST] do formulário é processado pela ação [Action10Post] a seguir:
// Ação10-POST
[HttpPost]
public ContentResult Action10Post(ViewModel10 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("Contrôleur={0}, Action={1}, valide={2}, erreurs={3}", RouteData.Values["controller"], RouteData.Values["action"], ModelState.IsValid, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
- linha 3: a ação [Action10Post] tem como modelo de entrada o formulário enviado;
- linha 5: recuperam-se os erros de validação desse formulário;
- linha 6: prepara-se a resposta em texto para o cliente;
- linha 7: ela é enviada.
Vamos agora examinar as propriedades do modelo [ViewModel10], uma a uma, e ver como os metadados associados influenciam o HTML gerado e a validação dos campos de preenchimento.
5.8.2. Propriedade [Text]
Definição
[Display(Name="Text")]
[DataType(DataType.Text)]
public string Text { get; set; }
...
Text = "tra la la";
Visão
<tr>
<td>@Html.LabelFor(m => m.Text)</td>
<td>@Html.EditorFor(m => m.Text)</td>
<td>@Html.DisplayFor(m => m.Text)</td>
</tr>
Visual
![]() |
HTML gerado
<tr>
<td><label for="Text">Text</label></td>
<td><input class="text-box single-line" id="Text" name="Text" type="text" value="tra la la" /></td>
<td>tra la la</td>
</tr>
Comentários
- O método [Html.LabelFor] gerou a tag <label> da linha 2. O valor do atributo [for] é o nome da propriedade de parâmetro do método [Html.LabelFor]
public string Text { get; set; }
O texto exibido entre o início e o fim da tag é o texto dos metadados
[Display(Name="Text")]
O método [Html.LabelFor] sempre procede dessa forma. Não voltaremos a abordar isso para as outras propriedades.
- O método [Html.EditorFor] gerou a tag <input> da linha 3. Observe-se que ela possui um atributo [class] que associa a classe CSS [text-box single-line] à tag. Os atributos [id] e [name] têm como valor o nome [Text] da propriedade “parâmetro” do método [Html.EditorFor]. O atributo [type] recebeu o valor [text] devido ao metadado
[DataType(DataType.Text)]
- o método [Html.DisplayFor] gerou o texto da linha 4. Esse é o valor da propriedade de parâmetro do método [Html.DisplayFor ]. Esse método é influenciado pelos metadados
[DataType(DataType.Text)]
, o que faz com que o valor seja exibido como texto sem formatação.
5.8.3. Propriedade [MultiLineText]
Definição
[Display(Name = "TextArea")]
[DataType(DataType.MultilineText)]
public string MultiLineText { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.MultiLineText)</td>
<td>@Html.EditorFor(m => m.MultiLineText)</td>
<td>@Html.DisplayFor(m => m.MultiLineText)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="MultiLineText">TextArea</label></td>
<td><textarea class="text-box multi-line" id="MultiLineText" name="MultiLineText">
ligne1
ligne2</textarea></td>
<td>ligne1
ligne2</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <textarea> da linha 3. Observe-se que ela possui um atributo [class] que associa a classe CSS [text-box multi-line] à tag. Os atributos [id] e [name] têm como valor o nome [MultiLineText] da propriedade “parâmetro” do método [Html.EditorFor]. É sempre assim. Não mencionaremos mais isso. A tag gerada é <textarea> devido ao metadado
[DataType(DataType.MultilineText)]
, que especificava que a propriedade era um texto de várias linhas.
- O método [Html.DisplayFor] gerou o texto das linhas 4 e 5. Esse é o valor da propriedade de parâmetro do método [Html.DisplayFor ].
5.8.4. Propriedade [Number]
Definição
[Display(Name = "Number")]
public int Number { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Number)</td>
<td>@Html.EditorFor(m => m.Number)</td>
<td>@Html.DisplayFor(m => m.Number)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Number">Number</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="Le champ Number doit être un nombre." data-val-required="Le champ Number est requis." id="Number" name="Number" type="number" value="4" /></td>
<td>4</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [number]. Aparentemente, simplesmente porque a propriedade tem o tipo [int]. Os atributos [data-val], [data-val-number] e [data-val-required] são atributos não reconhecidos pelo HTML5. Eles são utilizados por um framework JavaScript de validação de dados no lado do cliente;
- o método [Html.DisplayFor] gerou o texto da linha 4, o valor da propriedade.
Validação
Os atributos [data-x] influenciam a validação de dados no lado do cliente. Aqui estão dois exemplos:
Digita-se um número incorreto e valida-se:
![]() |
No exemplo acima, a validação ocorreu no lado do cliente. O formulário não será enviado até que o erro seja corrigido.
Outro exemplo: não se insere nada:
![]() |
No [1] acima, o [Action10Post] sinaliza um erro. Talvez nos lembremos de que já havíamos obtido esse comportamento ao usar o atributo [Required] na propriedade a ser controlada (ver página 69), neste caso, a propriedade [Number]. Aqui, não foi necessário fazer isso.
5.8.5. Propriedade [Decimal]
Definição
[Display(Name = "Decimal")]
[UIHint("Decimal")]
public double Decimal { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Decimal)</td>
<td>@Html.EditorFor(m => m.Decimal)</td>
<td>@Html.DisplayFor(m => m.Decimal)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Decimal">Decimal</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="Le champ Decimal doit être un nombre." data-val-required="Le champ Decimal est requis." id="Decimal" name="Decimal" type="text" value="10,20" /></td>
<td>10,20</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [text]. Os demais atributos são idênticos aos gerados para a propriedade [Number] anterior. Os metadados:
[UIHint("Decimal")]
faz com que o valor da propriedade seja exibido com duas casas decimais nos dois métodos [Html.EditorFor] e [Html.DisplayFor]
Validação
Nenhum erro de validação é sinalizado no lado do cliente, ao contrário do caso anterior. O erro é sinalizado apenas pela ação [Action10Post]. Mais uma vez, o número decimal é obrigatório, sem que seja necessário definir o atributo [Required].
5.8.6. Propriedade [Tel]
Definição
[Display(Name = "Tel")]
[DataType(DataType.PhoneNumber)]
public string Tel { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Tel)</td>
<td>@Html.EditorFor(m => m.Tel)</td>
<td>@Html.DisplayFor(m => m.Tel)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Tel">Tel</label></td>
<td><input class="text-box single-line" id="Tel" name="Tel" type="tel" value="0617181920" /></td>
<td>0617181920</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [tel]. Esse valor foi gerado devido aos metadados:
[DataType(DataType.PhoneNumber)]
O tipo [tel] para uma tag <input> é uma novidade em relação ao HTML5. O navegador Chrome a tratou como uma tag <input> com o tipo [text].
Validação
Não há erros de validação relatados nem no lado do cliente nem no lado do servidor. É possível inserir qualquer coisa.
5.8.7. Propriedade [Date]
Definição
[Display(Name = "Date")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Date)</td>
<td>@Html.EditorFor(m => m.Date)</td>
<td>@Html.DisplayFor(m => m.Date)</td>
</tr>
Imagem
![]() |
HTML gerado
<tr>
<td><label for="Date">Date</label></td>
<td><input class="text-box single-line" data-val="true" data-val-date="Le champ Date doit être une date." data-val-required="Le champ Date est requis." id="Date" name="Date" type="date" value="11/10/2013" /></td>
<td>11/10/2013</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [date]. Esse valor foi gerado devido aos metadados:
[DataType(DataType.Date)]
O tipo [date] para uma tag <input> é uma novidade em relação ao HTML5. O navegador Chrome o reconhece e permite inserir a data por meio de um calendário. Além disso, a data inserida é apresentada no formato [jj/mm/aaaa], ou seja, o Chrome adapta o formato da data ao formato [locale] do navegador.
- O método [Html.DisplayFor] também escreveu a data no formato [jj/mm/aaaa], sempre devido à presença do metadado [Date].
Validação
Uma data inválida é sinalizada no lado do cliente ([1]), impedindo o envio do POST do formulário para o servidor.
![]() |
A ausência de data não é sinalizada no lado do cliente, mas é no lado do servidor: [2].
5.8.8. Propriedade [Time]
Definição
[Display(Name = "Time")]
[DataType(DataType.Time)]
public DateTime Time { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Time)</td>
<td>@Html.EditorFor(m => m.Time)</td>
<td>@Html.DisplayFor(m => m.Time)</td>
</tr>
Visual
HTML gerado
<tr>
<td><label for="Time">Time</label></td>
<td><input class="text-box single-line" data-val="true" data-val-required="Le champ Time est requis." id="Time" name="Time" type="time" value="11:17" /></td>
<td>11:17</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [time]. Esse valor foi gerado devido aos metadados:
[DataType(DataType.Time)]
O tipo [time] para uma tag <input> é uma novidade em relação ao HTML5. O navegador Chrome o reconhece e permite inserir uma hora no formato [hh:mm];
- o método [Html.DisplayFor] também escreveu a hora no formato [hh:mm], sempre devido à presença do metadado [Time].
Validação
Tecnicamente, não é possível inserir uma hora inválida. A ausência de hora é sinalizada no servidor:
![]() |
5.8.9. Propriedade [HiddenInput]
Definição
[Display(Name = "HiddenInput")]
[UIHint("HiddenInput")]
public string HiddenInput { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.HiddenInput)</td>
<td>@Html.EditorFor(m => m.HiddenInput)</td>
<td>@Html.DisplayFor(m => m.HiddenInput)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="HiddenInput">HiddenInput</label></td>
<td>caché<input id="HiddenInput" name="HiddenInput" type="hidden" value="oculto" /></td>
<td>caché</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [hidden], ou seja, um campo oculto (mas, mesmo assim, enviado). Esse valor foi gerado devido aos metadados:
[UIHint("HiddenInput")]
- o método [Html.DisplayFor] escreveu o valor do campo oculto.
5.8.10. Propriedade [Boolean]
Definição
[Display(Name = "Boolean")]
public bool Boolean { get; set; }
Visualização
<tr>
<td>@Html.LabelFor(m => m.Boolean)</td>
<td>@Html.EditorFor(m => m.Boolean)</td>
<td>@Html.DisplayFor(m => m.Boolean)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Boolean">Boolean</label></td>
<td><input checked="checked" class="check-box" data-val="true" data-val-required="Le champ Boolean est requis." id="Boolean" name="Boolean" type="checkbox" value="true" /><input name="Boolean" type="hidden" value="false" /></td>
<td><input checked="checked" class="check-box" disabled="disabled" type="checkbox" /></td>
</tr>
Comentários
- o método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [checkbox], ou seja, uma caixa de seleção. Esse valor foi gerado porque a propriedade é booleana:
public bool Boolean { get; set; }
- o método [Html.DisplayFor] gerou a linha 4, também uma caixa de seleção (atributo type), mas desativada (atributo disabled).
5.8.11. Propriedade [Email]
Definição
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
public string Email{ get; set; }
Exibição
<tr>
<td>@Html.LabelFor(m => m.Email)</td>
<td>@Html.EditorFor(m => m.Email)</td>
<td>@Html.DisplayFor(m => m.Email)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Email">Email</label></td>
<td><input class="text-box single-line" id="Email" name="Email" type="email" value="x@y.z" /></td>
<td><a href="mailto:x@y.z">x@y.z</a></td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [email]. Esse tipo é novo no HTML5. Esse tipo foi gerado devido aos metadados:
[DataType(DataType.EmailAddress)]
O Chrome parece ter tratado esse tipo como um tipo [text].
- O método [Html.DisplayFor] gerou a linha 4: um link para o endereço de e-mail.
Validação
Um endereço inválido é sinalizado no lado do cliente [1]:
![]() |
A ausência de entrada não causa nenhum erro.
5.8.12. Propriedade [Url]
Definição
[Display(Name = "Url")]
[DataType(DataType.Url)]
public string Url { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Url)</td>
<td>@Html.EditorFor(m => m.Url)</td>
<td>@Html.DisplayFor(m => m.Url)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Url">Url</label></td>
<td><input class="text-box single-line" id="Url" name="Url" type="url" value="http://istia.univ-angers.fr" /></td>
<td><a href="http://istia.univ-angers.fr">http://istia.univ-angers.fr</a></td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [url]. Esse tipo é novo no HTML5. Ele foi gerado devido aos metadados:
[DataType(DataType.Url)]
O Chrome parece tratar esse tipo como um tipo [text].
- O método [Html.DisplayFor] gerou a linha 4: um link para o URL.
Validação
Um URL inválido é sinalizado no lado do cliente [1]:
![]() |
A ausência de entrada não causa nenhum erro.
5.8.13. Propriedade [Password]
Definição
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Password)</td>
<td>@Html.EditorFor(m => m.Password)</td>
<td>@Html.DisplayFor(m => m.Password)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Password">Password</label></td>
<td><input class="text-box single-line password" id="Password" name="Password" type="password" value="mdp" /></td>
<td>mdp</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [password]. Esse tipo foi gerado devido aos metadados:
[DataType(DataType.Password)]
- o método [Html.DisplayFor] gerou a linha 4.
5.8.14. Propriedade [Currency]
Definição
[Display(Name = "Currency")]
[DataType(DataType.Currency)]
public double Currency { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.Currency)</td>
<td>@Html.EditorFor(m => m.Currency)</td>
<td>@Html.DisplayFor(m => m.Currency)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="Currency">Currency</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="Le champ Currency doit être un nombre." data-val-required="Le champ Currency est requis." id="Currency" name="Currency" type="text" value="4,2" /></td>
<td>4,20 €</td>
</tr>
Comentários
- o método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [text];
- o método [Html.DisplayFor] gerou a linha 4, um número com duas casas decimais e um símbolo monetário. Esse formato foi utilizado devido aos metadados:
[DataType(DataType.Currency)]
Validação
Um valor inválido [1] ou a ausência de valor [2] é sinalizada no servidor:
![]() |
5.8.15. Propriedade [CreditCard]
Definição
[Display(Name = "CreditCard")]
[DataType(DataType.CreditCard)]
public string CreditCard { get; set; }
Visão
<tr>
<td>@Html.LabelFor(m => m.CreditCard)</td>
<td>@Html.EditorFor(m => m.CreditCard)</td>
<td>@Html.DisplayFor(m => m.CreditCard)</td>
</tr>
Imagem
HTML gerado
<tr>
<td><label for="CreditCard">CreditCard</label></td>
<td><input class="text-box single-line" id="CreditCard" name="CreditCard" type="text" value="0123456789012345" /></td>
<td>0123456789012345</td>
</tr>
Comentários
- O método [Html.EditorFor] gerou a tag <input> da linha 3 com um atributo [type] do tipo [text]. O método [Html.DisplayFor] gerou a linha 4. Não fica claro aqui o que os metadados trazem:
[DataType(DataType.CreditCard)]
Validação
Não é realizada nenhuma verificação, nem no lado do cliente, nem no lado do servidor.
5.9. Validação de um formulário
Já abordamos o problema da validação do modelo de uma ação no parágrafo 4.5 e nos parágrafos seguintes. Voltamos a tratar dessa questão no contexto de um formulário:
- como sinalizar os erros de preenchimento ao usuário;
- realizar as validações tanto no lado do cliente quanto no lado do servidor, a fim de sinalizar os erros mais rapidamente ao usuário.
5.9.1. Validação no lado do servidor
Consideremos o seguinte modelo:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Mail;
namespace Exemple_03.Models
{
public class ViewModel11 : IValidatableObject
{
[Required(ErrorMessage = "Information requise")]
[Display(Name = "Chaîne d'au moins quatre caractères")]
[RegularExpression(@"^.{4,}$", ErrorMessage = "Information incorrecte")]
public string Chaine1 { get; set; }
[Display(Name = "Chaîne d'au plus quatre caractères")]
[Required(ErrorMessage = "Information requise")]
[RegularExpression(@"^.{1,4}$", ErrorMessage = "Information incorrecte")]
public string Chaine2 { get; set; }
[Required(ErrorMessage = "Information requise")]
[Display(Name = "Chaîne de quatre caractères exactement")]
[RegularExpression(@"^.{4,4}$", ErrorMessage = "Information incorrecte")]
public string Chaine3 { get; set; }
[Required(ErrorMessage = "Information requise")]
[Display(Name = "Nombre entier")]
public int Entier1 { get; set; }
[Display(Name = "Nombre entier dans l'intervalle [1,100]")]
[Required(ErrorMessage = "Information requise")]
[Range(1, 100, ErrorMessage = "Information incorrecte")]
public int Entier2 { get; set; }
[Display(Name = "Nombre réel")]
[Required(ErrorMessage = "Information requise")]
public double Reel1 { get; set; }
[Display(Name = "Nombre réel dans l'intervalle [10.2, 11.3]")]
[Required(ErrorMessage = "Information requise")]
[Range(10.2, 11.3, ErrorMessage = "Information incorrecte")]
public double Reel2 { get; set; }
[Display(Name = "Adresse mail")]
[Required(ErrorMessage = "Information requise")]
public string Email1 { get; set; }
[Display(Name = "Date sous la forme dd/jj/aaaa")]
[RegularExpression(@"\s*\d{2}/\d{2}/\d{4}\s*", ErrorMessage = "Information incorrecte")]
[Required(ErrorMessage = "Information requise")]
public string Regexp1 { get; set; }
[Display(Name = "Date postérieure à celle d'aujourd'hui")]
[Required(ErrorMessage = "Information requise")]
[DataType(DataType.Date)]
public DateTime Date1 { get; set; }
// validação
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> résultats = new List<ValidationResult>();
// Data 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
}
// E-mail 1
try
{
new MailAddress(Email1);
}
catch
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
}
// retorna a lista de erros
return résultats;
}
}
}
Esse modelo será exibido pela seguinte visualização [Action11Get.cshtml]:
![]() |
@model Exemple_03.Models.ViewModel11
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action11Get</title>
<link rel="stylesheet" href="~/Content/Site.css" />
</head>
<body>
<h3>Formulaire ASP.NET MVC – Validation 1</h3>
@using (Html.BeginForm("Action11Post", "First"))
{
<table>
<thead>
<tr>
<th>Type attendu</th>
<th>Valeur saisie</th>
<th>Message d'erreur</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Html.LabelFor(m => m.Chaine1)</td>
<td>@Html.EditorFor(m => m.Chaine1)</td>
<td>@Html.ValidationMessageFor(m => m.Chaine1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Chaine2)</td>
<td>@Html.EditorFor(m => m.Chaine2)</td>
<td>@Html.ValidationMessageFor(m => m.Chaine2)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Chaine3)</td>
<td>@Html.EditorFor(m => m.Chaine3)</td>
<td>@Html.ValidationMessageFor(m => m.Chaine3)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Entier1)</td>
<td>@Html.EditorFor(m => m.Entier1)</td>
<td>@Html.ValidationMessageFor(m => m.Entier1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Entier2)</td>
<td>@Html.EditorFor(m => m.Entier2)</td>
<td>@Html.ValidationMessageFor(m => m.Entier2)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Reel1)</td>
<td>@Html.EditorFor(m => m.Reel1)</td>
<td>@Html.ValidationMessageFor(m => m.Reel1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Reel2)</td>
<td>@Html.EditorFor(m => m.Reel2)</td>
<td>@Html.ValidationMessageFor(m => m.Reel2)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Email1)</td>
<td>@Html.EditorFor(m => m.Email1)</td>
<td>@Html.ValidationMessageFor(m => m.Email1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Regexp1)</td>
<td>@Html.EditorFor(m => m.Regexp1)</td>
<td>@Html.ValidationMessageFor(m => m.Regexp1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Date1)</td>
<td>@Html.EditorFor(m => m.Date1)</td>
<td>@Html.ValidationMessageFor(m => m.Date1)</td>
</tr>
</tbody>
</table>
<p>
<input type="submit" value="Valider" />
</p>
}
</body>
</html>
- linha 12: é feita a referência à folha de estilo [Site.css]. Ela contém, por padrão, classes utilizadas para destacar os erros de preenchimento do formulário;
- linhas 18-25: uma tabela com três colunas:
- a coluna 1 exibe texto com o método [Html.LabelFor],
- a coluna 2 exibe o dado inserido com o método [Html.EditorFor],
- a coluna 3 exibe o eventual erro de preenchimento com o método [Html.ValidationMessageFor];
A ação [Action11Get] serve para exibir o formulário:
// Ação11-GET
[HttpGet]
public ViewResult Action11Get()
{
return View("Action11Get", new ViewModel11());
}
A ação [Action11Post] serve para exibir novamente o formulário com os eventuais erros de preenchimento:
// Ação11-POST
[HttpPost]
public ViewResult Action11Post(ViewModel11 modèle)
{
return View("Action11Get", modèle);
}
- linha 3: o modelo [ViewModel11] é criado e, em seguida, inicializado com os valores enviados. Nesse momento, podem ocorrer erros. A cada propriedade P incorreta do modelo está associada uma mensagem de erro. É essa mensagem que o método [Html.ValidationMessageFor] do formulário permite obter.
Veja um exemplo de execução:
![]() | ![]() |
Veja outro exemplo:
![]() |
Observe que ambas as datas estão incorretas (hoje é 11/10/2013), mas os erros não são sinalizados. Esses erros são detectados pelo método [Validate] do modelo:
// validação
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> résultats = new List<ValidationResult>();
// Data 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
}
// E-mail 1
try
{
new MailAddress(Email1);
}
catch
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
}
// Regexp1
try
{
DateTime.ParseExact(Regexp1, "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("fr-FR"));
}
catch
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Regexp1" }));
}
// exibe a lista de erros
return résultats;
}
O método [Validate] só é executado quando todas as validações por atributos forem aprovadas. É o que mostra um último exemplo:
![]() |
5.9.2. Validação no lado do cliente
Todas as validações anteriores foram realizadas no lado do servidor. Portanto, é necessária uma troca de dados entre o cliente e o servidor para que o usuário perceba seus erros. A validação do lado do cliente utiliza código JavaScript para sinalizar ao usuário seus erros o mais cedo possível e, em qualquer caso, antes do POST. Este só pode ocorrer quando todos os erros detectados tiverem sido corrigidos.
Retomamos o modelo [ViewModel11] anterior, mas agora o exibimos com a seguinte visualização [Action12Get.cshtml]:
![]() |
@model Exemple_03.Models.ViewModel11
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action12Get</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>
</head>
<body>
<h3>Formulaire ASP.NET MVC - Validation 1</h3>
@using (Html.BeginForm("Action11Post", "First"))
{
<table>
<thead>
<tr>
<th>Type attendu</th>
<th>Valeur saisie</th>
<th>Message d'erreur</th>
</tr>
</thead>
<tbody>
...
</tbody>
</table>
<p>
<input type="submit" value="Valider" />
</p>
}
</body>
</html>
Observação: linha 13, adapte a versão do jQuery à que você possui com a sua versão do Visual Studio (veja a seguir).
A validação do lado do cliente requer a presença da linha 3 abaixo no arquivo [Web.config] do aplicativo.
<appSettings>
...
<add key="ClientValidationEnabled" value="true" />
</appSettings>
- linhas 1-4: a seção [appSettings] deve ser um elemento filho direto da seção [configuration] do arquivo [Web.config];
A visualização [Action12Get] é idêntica à visualização [Action11Get] anterior, com exceção das linhas 13 a 15. Essas linhas incluem na visualização os scripts JavaScript necessários para a validação do lado do cliente. Esses scripts estão localizados na pasta [Scripts] do projeto:
![]() |
Cada script possui uma versão normal ([.js]) e uma versão minificada ([min.js]). Esta última versão é mais leve, mas ilegível. Ela é utilizada em produção. A versão legível é utilizada em desenvolvimento.
A visualização [Action12Get.cshtml] será exibida pela ação [Action12Get] a seguir:
// Ação12-GET
[HttpGet]
public ViewResult Action12Get()
{
return View("Action12Get", new ViewModel11());
}
O formulário preenchido será processado pela ação [Action12Post] a seguir:
// Ação12-POST
[HttpPost]
public ViewResult Action12Post(ViewModel11 modèle)
{
return View("Action12Get", modèle);
}
Vamos ver como isso funciona com um exemplo:
![]() |
Assim que digitamos um caractere em [1], a mensagem em [2] é exibida porque o valor esperado deve ter pelo menos quatro caracteres. Assim, a validação é feita a cada novo caractere digitado. A mensagem de erro desaparece ao digitar o quarto caractere. Feito isso, vamos validar o formulário:
![]() |
O URL [3] nos mostra que o [POST] não ocorreu. Mas ao clicar no botão [Valider], todas as validações do lado do cliente foram acionadas e novas mensagens de erro apareceram.
Vejamos, por exemplo, o código HTML gerado para a primeira entrada:
<tr>
<td><label for="Chaine1">Chaîde pelo menos quatro caracteres</label></td>
<td><input class="text-box single-line" data-val="true" data-val-regex="Information incorrecte" data-val-regex-pattern="^.{4,}$" data-val-required="Information requise" id="Chaine1" name="Chaine1" type="text" value="" /></td>
<td><span class="field-validation-valid" data-valmsg-for="Chaine1" data-valmsg-replace="true"></span></td>
</tr>
- na linha 3, encontramos:
- a mensagem de erro para o caso em que a entrada está faltando [data-val-required],
- a mensagem de erro para o caso em que a entrada está incorreta [data-val-regex],
- a expressão regular para a sequência digitada [data-val-regex-pattern];
- linha 4, outros atributos [data-x] utilizados para exibir a eventual mensagem de erro;
Os atributos [data-x] das tags geradas são utilizados pelo JavaScript que incorporamos na visualização. Se ele estiver ausente, esses atributos são simplesmente ignorados e, nesse caso, não há validação do lado do cliente. O funcionamento é semelhante ao do exemplo anterior. Daí a denominação [unobtrusive] para essa técnica.
5.10. Gerenciamento de links de navegação e de ação
Vamos criar as duas visualizações a seguir para ilustrar o gerenciamento de links em uma visualização:
![]() |
- em [1] e [2], temos dois links de navegação;
- na [3], há um link de ação que envia o formulário. Ele não serve para navegar.
A página 1 é gerada pela seguinte vista [Action16Get.cshtml]:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action16Get</title>
<script>
function postForm() {
// recupera-se o formulário do documento
var form = document.forms[0];
// envio
form.submit();
}
</script>
</head>
<body>
<h3>Navigation - page 1</h3>
<h4>@ViewBag.info</h4>
@using (Html.BeginForm("Action16Post", "Second"))
{
@Html.Label("data", "Tapez un texte")
@Html.TextBox("data")
<a href="javascript:postForm()">Valider</a>
}
<p>
@Html.ActionLink("Page 2", "Action17Get", "Second")
</p>
</body>
</html>
- linha 22: uma informação inicializada pela ação que irá gerar a visualização;
- linhas 23-28: um formulário;
- linha 25: um rótulo para o campo [data];
- linha 26: um campo de entrada denominado [data];
- linha 27: um link do tipo [submit]. Ao clicar nele, a função JavaScript [postForm] é executada (atributo href). Essa função está definida nas linhas 12 a 17;
- linha 14: obtém-se uma referência ao primeiro formulário do documento, o da linha 23;
- linha 16: esse formulário é enviado. No final, tudo ocorre como se tivéssemos clicado em um botão do tipo [submit]. O formulário é enviado ao controlador e à ação especificados na linha 23;
- linha 30: um link de navegação. O código HTML gerado é o seguinte:
<a href="/Second/Action17Get">Page 2</a>
O método utilizado é ActionLink(Texto, Ação, Controlador).
A página 2 é gerada pela seguinte vista [Action17Get.cshtml]:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action17Get</title>
</head>
<body>
<h3>Navigation - Page 2</h3>
<h4>@ViewBag.info</h4>
<p>
@Html.ActionLink("Page 1", "Action16Get", "Second")
</p>
</body>
</html>
As ações que geram essas visualizações são as seguintes:
// Ação16-GET
[HttpGet]
public ViewResult Action16Get()
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View("Action16Get");
}
// Ação16-POST
[HttpPost]
public ViewResult Action16Post(string data)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}, Data={2}", RouteData.Values["controller"], RouteData.Values["action"], data);
return View("Action16Get");
}
// Ação17-GET
[HttpGet]
public ViewResult Action17Get()
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View();
}
- na linha 6, a ação [Action16Get] gera a visualização [Action16Get.cshtml], ou seja, a página 1 do exemplo. Essa visualização tem como modelo a [ViewBag] (linha 5);
- linha 19, a ação [Action17Get] gera a visualização [Action17Get.cshtml], ou seja, a página 2 do exemplo. Essa visualização tem como modelo a [ViewBag] (linha 21);
- linha 11: a ação [Action16Post] processa o POST do formulário da visualização [Action16Get.cshtml]. Ela recebe o parâmetro denominado [data]. Lembramos que esse é o nome do campo de entrada no formulário;
- linha 13: uma informação é inserida no [ViewBag];
- linha 14: a visualização [Action16Get.cshtml] é exibida.
O leitor é convidado a testar este exemplo.































































