5. A vista e o seu modelo
5.1. Introduction
Voltemos à arquitetura de uma aplicação ASP.NET MVC:
![]() |
No capítulo anterior, analisámos como o ASP.NET MVC apresentava as informações do pedido [1] a uma ação [2a] sob a forma de um modelo que podia conter restrições de validação. Este modelo era fornecido como entrada para a ação e denominámo-lo «modelo da ação». Vamos agora centrar-nos no resultado mais comum de uma ação, o tipo [ViewResult], que corresponde a uma vista V [3] acompanhada do seu modelo M [2c]. Este modelo será designado por «modelo da vista V», não devendo ser confundido com o modelo da ação que acabámos de analisar. Um é uma entrada da ação, o outro é uma saída.
Comecemos por criar um novo projeto [Exemple-03] [1], sempre dentro da mesma solução, do tipo básico ASP.NET MVC:
![]() |
Vamos criar um controlador denominado [First] [2]. O código gerado para este 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) devolve um tipo [ViewResult] que deriva de [ActionResult]. Este método admite várias sobrecargas. Vamos ver algumas delas. A principal é a seguinte:
![]() |
- o primeiro parâmetro é o nome da vista. Se estiver ausente, a vista utilizada é aquela que tem o mesmo nome que a ação que produz 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 tem modelo.
O método [Index] abaixo:
public ActionResult Index()
{
return View();
}
solicita que a vista [/Views/First/Index.cshtml] seja apresentada. Não lhe transmite qualquer modelo. Vamos criar [1] na pasta [/Views/First]:
![]() |
e, em seguida, criemos nela a vista [Index] [2]:
![]() |
![]() |
Indica-se o nome da vista como [3]. Esta é 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 código HTML clássico, exceto pelas linhas 1 a 3, que são código C#. O programa que gere as vistas é denominado motor de vistas. Este encarrega-se de gerir tudo o que não é HTML para o converter em HTML. No final, de facto, é isso que será enviado ao cliente. O motor de vistas chama-se aqui [Razor]. Permite incluir código C# numa vista. O [Razor] irá 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# numa vista:
- a transição do HTML para o C# ocorre ao encontrar o caractere @ (linha 1). Se este caractere introduzir um bloco de código, colocar-se-ão as chaves (linhas 1 e 3). Se introduzir uma variável cujo valor se pretenda recuperar, escrever-se-á simplesmente @variável;
- a transição do C# para o HTML ocorre ao encontrar o caractere < (linha 5). Por vezes, é necessário forçar esta transição, nomeadamente quando se inclui na página texto simples sem a baliza HTML. Nesse caso, utilizar-se-á a baliza <text> para inserir o texto: <text>aqui vai o texto simples</text>.
A linha 2 acima indica que a vista [Index] não tem uma página-mestre.
Alteremos a vista da seguinte forma:
@{
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: apresenta o valor dessa variável.
Vamos agora consultar 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. Utilizar o [ViewBag] para passar informações para a vista
Criamos uma nova ação chamada [Action01] associada à vista [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, tal como se faz na linha 4. Este objeto tem a particularidade de também ser acessível à vista. É, portanto, uma forma de lhe transmitir informação;
- linha 5: é solicitada a vista predefinida da ação. Trata-se da vista [/First/Action01.cshtml]. Não lhe é transmitido nenhum modelo.
A vista [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] é apresentada.
Vamos testar. Solicitamos o URL [/First/Action01]:
![]() |
5.3. Utilizar um modelo fortemente tipado para passar informações para a vista
O método anterior tem a desvantagem de não permitir a deteção de erros antes da execução. Assim, se a vista [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] chama-se [info]. É possível, então, utilizar um modelo fortemente tipado para evitar este inconveniente.
Num 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) sob a forma de uma cadeia de caracteres. Vamos transmitir estas informações num modelo de vista [ViewModel01]. Como este modelo retoma informações de [ActionModel03], vamos derivá-lo desta classe.
Começamos por copiar o [ActionModel03] do projeto [Exemple-02] para o projeto [Exemple-03] atual:
![]() |
e alteramos o seu espaço de nomes para que corresponda ao 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-lhe a propriedade [Erreurs].
Escrevemos agora a ação [Action02] que:
- aceita como entrada o modelo de ação [ActionModel03];
- e devolve como saída o modelo de vista [ViewModel01].
O seu código é o seguinte:
// Ação 02
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]. Devolve um resultado do tipo [ViewResult];
- linha 4: os erros relacionados com o modelo de ação [ActionModel03] são agregados na cadeia 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. Este é o modelo da vista. A vista não é especificada. Por conseguinte, será utilizada a vista por predefiniçã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.
Construímos agora a vista [/First/Action02.cshtml]:
![]() |
O 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 reside na linha 1. A notação [@model] define o tipo do modelo da vista. Este modelo é, em seguida, referenciado pela notação [@Model] (linhas 16-21);
- linhas 15-22: as informações do modelo são apresentadas numa 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 vista [ViewModel01] retoma as informações do modelo de ação [ActionModel03]. Isto acontece frequentemente. É então possível utilizar um único modelo que servirá tanto de modelo de ação como de vista. Criamos um novo modelo [ActionModel04]:
![]() |
que terá o seguinte aspeto:
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; }
// ---------------------- vista --------------------------------
public string Erreurs { get; set; }
}
}
- linhas 8-28: o modelo da ação com as suas restrições de integridade. Estes campos também farão parte da vista;
- linha 31: uma propriedade específica do modelo da vista. 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 utiliza esse mesmo modelo como modelo de vista;
- linha 4: complementada com a informação [Erreurs];
Resta-nos apenas criar a vista [/First/Action03.cshtml]:
![]() |
- em [1]: clique com o botão direito do rato no código de [Action03] e, em seguida, em [Ajouter une vue];
- em [2]: o nome da vista proposto por predefinição;
- em [3]: indicar que se está a criar uma vista fortemente tipada;
- em [4]: selecionar a classe correta na lista suspensa, neste caso a classe [ActionModel04];
- em [5]: a vista criada.
Atribuímos à vista [Action03] o mesmo código que à vista [Action02]. Apenas o modelo da vista (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, executemos a ação [Action03] sem parâmetros:
![]() |
Os resultados são os mesmos de antes. É frequente utilizar o mesmo modelo para a ação e para a vista, uma vez que o modelo da vista retoma frequentemente informações do modelo da ação. Utiliza-se, assim, um modelo mais abrangente, que pode ser utilizado tanto pela ação como pela vista que esta gera. Deve-se 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 utilizador bem informado poderia inicializar partes do modelo da vista sem o nosso conhecimento.
5.4. [Razor] – primeiros passos
Vamos agora apresentar alguns elementos das vistas [Razor], principalmente as instruções foreach e if.
Suponhamos que se pretenda apresentar uma lista de pessoas numa tabela HTML. O modelo da vista 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-16;
- linhas 6-10: o construtor do modelo inicializa a propriedade [Personnes] da linha 5 com um tabuleiro de duas pessoas.
A ação que produz este modelo na saída será a seguinte: [Action04]:
// Ação04
public ViewResult Action04()
{
return View(new ViewModel02());
}
- linha 2: a ação não tem nenhum modelo de entrada;
- linha 4: passa para a sua vista por predefinição, uma instância do modelo [ViewModel02] que acabámos de definir.
A vista [Action04.cshtml] irá apresentar o modelo [ViewModel02]:
![]() |
O código da vista [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 vista;
- linha 2: a importação do espaço de nomes da classe [Personne] utilizada na linha 24;
- linhas 16-32: o array HTML que apresenta as pessoas do modelo;
- linha 24: o início do código C# é assinalado pelo caractere @. A instrução [foreach] irá percorrer todas as pessoas do modelo;
- linhas 26-27: o carácter < interrompe o C# e dá início ao HTML. Em seguida, volta a aparecer o carácter @ para alternar para o C# e escrever o nome da pessoa. Depois, volta a aparecer o carácter <, que faz a alternância 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 vista podem ser preenchidos a partir de uma coleção: listas, com ou sem menu suspenso, botões de opção e caixas de seleção. Consideremos o seguinte exemplo novo, que apresenta 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 vista;
- linha 5: a lista de pessoas a apresentar na lista suspensa, na forma [Prénom Nom];
- linha 6: o [Id] da pessoa a selecionar 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 vista [Action05.cshtml] irá apresentar este modelo:
![]() |
O 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. Recorde-se que são as seguintes:
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 numa lista os textos contidos entre as balizas <option>...</option> |
atributos | name="cmbValeurs": nome do controlo. size="1": número de elementos da lista visíveis. size="1" transforma a lista no equivalente a uma caixa de seleção. selected="selected": se esta palavra-chave estiver presente para um elemento da lista, este aparece selecionado na lista. No nosso exemplo acima, o elemento da lista choix2 aparece como o elemento selecionado da caixa de seleção quando esta é apresentada pela primeira vez. value=”v”: se o elemento for selecionado pelo utilizador, é este valor [v] que é enviado para o servidor. Na ausência deste atributo, é o texto apresentado e selecionado que é enviado para o servidor. |
O código das linhas 17-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 é a que deve ser selecionada. Se for o caso, prepara-se o texto selected="selected", que deve ser inserido na baliza <option>;
- linha 24: a baliza <option> é gravada.
Solicitemos a ação [Action05]:
![]() |
- em [1,2], as pessoas são apresentadas na forma [Prénom Nom];
- No [1,2], a pessoa selecionada é aquela cujo [Id] é igual a 2.
Analisemos agora 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 efetivamente a pessoa de [Id]=2 que foi selecionada.
Os dois exemplos acima são suficientes. Ao escrever uma vista [Razor], é preciso resistir à tentação de incluir lógica nela. O código C# permitir-nos-ia fazê-lo. No entanto, no modelo MVC, a lógica deve estar na ação ou nas camadas inferiores [Metier, DAO], mas não na vista. Mesmo respeitando o modelo MVC, podemos acabar por ter muita lógica na vista para calcular valores intermédios. Isso pode significar que o modelo utilizado não é suficientemente detalhado. Este deve conter os valores finais de que a vista necessita, para que esta não tenha de os calcular por si própria. Uma boa vista é aquela em que há um mínimo de lógica e em que a estrutura HTML da vista permanece clara. Se se inserir demasiado código C#, a estrutura HTML pode tornar-se ilegível.
No exemplo acima, a lista suspensa poderia ser utilizada por um utilizador e, nesse caso, gostaríamos de saber que pessoa ele selecionou. Para isso, precisamos de um formulário.
5.5. Formulário – primeiros passos
O formulário apresentado ao utilizador será o seguinte:
![]() |
O modelo da vista será o modelo [ViewModel05], já utilizado anteriormente. A ação que irá apresentar esta vista 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 vista [/First/Action06Get.cshtml] será apresentada com uma instância do tipo [ViewModel05] como modelo.
A vista [/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 introduzidas por um utilizador, é necessário um formulário. É a baliza <form> nas linhas 18 e 31 que o delimita.
A baliza HTML <form> foi apresentada no parágrafo 2.5.2.1. Recorde-se as 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 recolhidos no formulário action="..." : URL para onde serão enviados os valores recolhidos no formulário. Um formulário Web é delimitado pelas balizas <form>...</form>. O formulário pode ter um nome (name="xx"). É o caso de todos os controlos que se podem encontrar num formulário. O objetivo de um formulário é recolher as informações fornecidas pelo utilizador através do teclado/rato e enviá-las para um URL de servidor Web. Qual? Aquele referenciado no atributo action="URL". Se este atributo estiver ausente, as informações serão enviadas para o URL do documento no qual o formulário se encontra. Um cliente Web pode utilizar dois métodos diferentes, denominados POST e GET, para enviar dados para um servidor Web. O atributo method="méthode", com method igual a GET ou POST, da baliza <form> indica ao navegador o método a utilizar para enviar as informações recolhidas no formulário para o URL especificado pelo atributo action="URL". Quando o atributo method não é especificado, o método GET é utilizado por predefinição. |
- linha 18: verifica-se que os valores do formulário serão enviados para o URL [/First/Action06] através de um comando HTTP POST;
- linha 30: um formulário deve ter um botão do tipo [submit]. É este botão que aciona o envio dos valores introduzidos para o URL, especificado pelo atributo [action] da baliza <form>.
O que é que o navegador irá transmitir exatamente quando o utilizador clicar no botão [Valider]? Isto foi explicado no parágrafo 2.5.3.1. Recorde-se o que foi dito:
controlo HTML | visual | valor(es) devolvido(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 selecionado pelo utilizador. | |
<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 utilizador | |
<input type="text" name="txtSaisie" size="20" value="algumas palavras"/> | txtSaisida=programação+Web - texto digitado pelo utilizador no campo de introdução. Os espaços foram substituídos pelo sinal + | |
<input type="password" name="txtMdp" size="20" value="unMotDePasse"/> | txtMdp=istoé-secreto - texto digitado pelo utilizador no campo de introdução | |
<textarea rows="2" name="areaSaisie" cols="20"> linha1 linha 2 linha 3 </textarea> | areaSaisie=os+fundamentos+da%0D%0A programação+Web - texto digitado pelo utilizador no campo de introdução. %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ção 3</option> </select> | cmbValores=3 - atributo [value] do elemento selecionado pelo utilizador | |
<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 utilizador |
<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 utilizador | |
<input type="submit" value="Enviar" name="cmdRenvoyer"/> | cmdRenvoyer=Enviar - nome e atributo value do botão utilizado para enviar os dados do formulário para o servidor | |
<input type="hidden" name="secret" value="uneValeur"/> | secret=umValor - atributo value do campo oculto |
No nosso formulário, temos duas tags que podem enviar um valor:
<select name="personneId">
...
</select>
e
<input name="valider" type="submit" value="Valider" />
Se o utilizador selecionar a pessoa n.º 2, os valores serão enviados da seguinte forma:
Os nomes dos parâmetros correspondem aos atributos [name] das balizas abrangidas pelo POST. Sem este atributo, as balizas não enviam qualquer 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. Neste caso, esta informação não nos interessa. Por vezes, os formulários têm vários botões do tipo [submit]. Nesse caso, é importante saber qual o botão em que se clicou. Por isso, atribuir-se-á o atributo [name] aos diferentes botões.
A baliza <select> é composta por uma sequência de balizas <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 deste atributo, é o texto exibido pela opção, por exemplo, [Pierre Martino], que é enviado.
A cadeia
será publicada na seguinte URL [/First/Action06]:
// Ação06-POST
[HttpPost]
public ViewResult Action06(ActionModel06 modèle)
{
return View("Action06Post",modèle);
}
Talvez se lembrem 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 não processem os mesmos comandos HTTP:
- A ação [Action06] da linha 3 gere uma ação POST (linha 2);
- A ação [Action06] da linha c gere uma ação GET (linha b).
A ação [Action06], que gere o POST, irá receber a seguinte cadeia de parâmetros:
Precisamos de um modelo de ação para encapsular estes 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 este modelo e transmite-o tal como está para a vista [Action06Post] (linha 5 da ação) seguinte:
![]() |
@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 é apresentado nas linhas 18 e 19.
Vejamos um exemplo:
![]() |
Em [1], seleciona-se a terceira pessoa de [Id], igual a 3. Em [2], envia-se o formulário. Em [3], os valores recebidos. Em [4,5], verifica-se que o mesmo URL foi chamado, um por um GET [4], e a outra por um POST e um [5]. Isto não é visível no URL.
Na vista apresentada a seguir ao POST, poder-se-ia preferir os nom e prénom da pessoa selecionada, em vez do seu número. É, portanto, necessário atualizar a visualização do POST e o seu modelo.
Criamos uma ação [Action07] para tratar deste caso. Esta ação terá de utilizar a sessão do utilizador para armazenar a lista de pessoas. Vamos seguir o modelo analisado no parágrafo 4.10, que permite incluir os dados de âmbito [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 guardará a lista de pessoas apresentadas na lista suspensa;
Temos de associar o tipo anterior [SessionModel] a um binder a que chamaremos [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 os seus modelos binder e [SessionModelBinder] é feita no [Global.asax]:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
// ligadores de modelos
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: é criada uma instância do tipo [SessionModel] na sessão, associada à chave [data].
Feito isto, 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 âmbito [Session] associado à chave [data];
- linha 5: cria-se o modelo da vista;
- linha 6: insere-se na sessão a tabela de pessoas. Iremos precisar dela na consulta seguinte, a do POST. O protocolo HTTP é um protocolo sem estado. É necessário utilizar uma sessão para dispor de memória entre as consultas. Uma sessão é específica de um utilizador e é gerida pelo servidor web;
- linha 7: é apresentada a vista [Action07Get.cshtml]. É 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>
É idêntica à vista [Action06Get.cshtml] já analisada. A principal diferença está na linha 7: a URL para a qual serão enviados os valores do formulário. Estes serão processados pela ação [Action07] seguinte:
// 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 âmbito [Session] associado à chave [data];
- linha 5: uma consulta LINQ recupera a pessoa com o [Id] que foi publicado;
- linha 6: constrói-se a cadeia de caracteres que deve ser apresentada pela vista [Action07Post] (linha 8);
- linha 7: para chamar o construtor correto [View], é necessário converter o tipo [string] para [object].
A vista [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 cadeia de caracteres é apresentada.
Eis um exemplo de execução:
![]() | ![]() |
5.6. Formulário – um exemplo completo
No parágrafo 2.5.2.1, analisámos o seguinte formulário HTML:
1 ![]() |
Vamos analisar uma ação [Action08Get] que apresenta (GET) este formulário e uma ação [Action08Post] que processa (POST) os valores introduzidos pelo utilizador. Um esquema clássico.
O modelo da vista [1] acima será uma instância da classe [ViewModel08]. Esta classe será simultaneamente:
- o modelo da vista gerada por um GET na ação [Action08Get];
- o modelo da ação [Action08Post] para um pedido POST.
![]() |
![]() |
5.6.1. O modelo de âmbito [Application]
Vamos supor que os elementos apresentados pelos botões de opção, pelas caixas de seleção e pelas várias listas são dados do âmbito [Application]. Um caso frequente. Estas informações provêm de um ficheiro de configuração ou de uma base de dados utilizados no arranque da aplicação, no método [Application_Start] de [Global.asax]. Este método evolui da seguinte forma:
protected void Application_Start()
{
....
// ligadores de modelos
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 iremos descrever em breve, está associado ao ligador de dados [ApplicationModelBinder], que já apresentámos na página 82;
- linha 10: uma instância do tipo [ApplicationModel] é registada no dicionário da aplicação, associada à chave [data].
A classe [ApplicationModel] serve para encapsular todos os dados do âmbito [Application]. Aqui, irá encapsular os dados que o formulário deve apresentar:
![]() |
namespace Exemple_03.Models
{
public class ApplicationModel
{
// as coleções a apresentar 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 dos 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 é selecionado;
- linha 6: a coleção apresentada pelo botão de opção;
- linha 7: a coleção apresentada pelas caixas de seleção;
- linha 8: a coleção apresentada pela lista suspensa;
- linha 9: a coleção apresentada pela lista de seleção única;
- linha 10: a coleção apresentada pela lista de seleção múltipla;
- linhas 13-43: estas coleções são inicializadas pelo construtor sem parâmetros da classe.
As diferentes coleções irão alimentar o seguinte formulário:
![]() |
5.6.2. O modelo da ação [Action08Get]
O formulário anterior será apresentado 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] só responderá a um comando [GET];
- linha 3: recebe como parâmetro o modelo da aplicação que acabámos de descrever;
- linha 5: inicializa uma informação no contentor dinâmico [ViewBag];
- linha 6: apresenta a vista [/First/Formulaire.cshtml] com o modelo [ViewModel08]. Este modelo será o do formulário apresentado anteriormente. Para tal, passa-se ao construtor o modelo da aplicação que define os elementos a apresentar.
5.6.3. O modelo da vista [Formulaire]
A classe [ViewModel08] será o modelo do formulário. Esta 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 introdução de dados
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 apresentar 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" };
}
}
}
- num formulário, existem dois tipos de elementos: aqueles que são apresentados e aqueles que são objeto de introdução de dados;
- as linhas 20-24 definem os elementos a exibir. Trata-se das diferentes coleções do formulário. Estas encontram-se no modelo da aplicação (linhas 34-38);
- linhas 10-17: definem os campos de introdução de dados do formulário;
- linha 10: [RadioButtonField] irá recuperar o valor introduzido nas 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>
Note-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 cadeia de caracteres com o seguinte formato:
param1=val1&RadioButtonField=2¶m2=val2
se o utilizador tiver marcado a opção denominada [non]. Na verdade, é o atributo [value] da opção marcada que é enviado.
- linha 11: [CheckBoxesField] irá 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>
Note-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 cadeia de caracteres com o seguinte formato:
param1=val1&CheckBoxesField=2&CheckBoxesField=3¶m2=val2
se o utilizador tiver marcado as caixas de seleção denominadas [2] e [3]. É o atributo [value] das opções marcadas que é enviado. Como podem ser enviados vários parâmetros com o mesmo nome, [CheckBoxesField] é uma matriz de valores e não um valor simples. Se nenhuma caixa estiver marcada, o parâmetro [CheckBoxesField] estará ausente da cadeia enviada e a propriedade com o mesmo nome do modelo não será inicializada. Isto pode ser incómodo, como veremos.
- linha 12: [TextField] irá recuperar o valor enviado pelas seguintes linhas do formulário:
<!-- o campo de introdução 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 introdução de dados é o nome da propriedade que vai ser inicializada. Nos dados enviados, encontrar-se-á uma cadeia de caracteres com o seguinte formato:
param1=val1&TextField=abcdef¶m2=val2
se o utilizador tiver introduzido [abcdef] no campo de introdução.
- linha 13: [PasswordField] irá recuperar o valor introduzido nas linhas seguintes do formulário:
<!-- o campo de introdução de palavra-passe -->
<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 introdução é o nome da propriedade que vai ser inicializada. Nos dados enviados, encontrar-se-á uma cadeia de caracteres com o seguinte formato:
param1=val1&PasswordField=abcdef¶m2=val2
se o utilizador tiver introduzido [abcdef] no campo de introdução.
- linha 14: [TextAreaField] irá recuperar o valor enviado pelas seguintes linhas do formulário:
<!-- o campo de introdução de texto multilinha -->
<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 introdução é o nome da propriedade que vai ser inicializada. Nos dados enviados, encontrar-se-á uma cadeia de caracteres com o seguinte formato:
param1=val1&TextAreaField=abcdef%0D%OAhijk¶m2=val2
se o utilizador tiver introduzido [abcdef] seguido de um salto de linha e de [ijk] no campo de introdução.
- linha 15: [DropDownListField] irá 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 baliza <select> é o nome da propriedade que vai ser inicializada. Nos dados enviados, encontrar-se-á uma cadeia de caracteres com o seguinte formato:
param1=val1&DropDownListField=1¶m2=val2
se o utilizador tiver selecionado a opção [choix1]. É o atributo [value] da opção selecionada que é enviado.
- linha 16: [SingleChoiceListField] irá recuperar o valor enviado pelas seguintes linhas do formulário:
<!-- a lista de escolha ú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 baliza <select> é o nome da propriedade que vai ser inicializada. É o atributo [size="3"] que faz com que não haja uma lista suspensa. Nos dados enviados, encontrar-se-á uma cadeia de caracteres com o seguinte formato:
param1=val1&SimpleChoiceListField=3¶m2=val2
se o utilizador tiver selecionado a opção [liste3]. É o atributo [value] da opção selecionada que é enviado. O parâmetro [SingleChoiceListField] pode estar ausente da cadeia enviada se nenhum elemento tiver sido selecionado.
- linha 17: [MultipleChoiceListField] irá recuperar os valores enviados pelas seguintes linhas do formulário:
<!-- a lista de escolha múltipla -->
<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 ao utilizador selecionar vários elementos mantendo premida a tecla [Ctrl]. Nos dados enviados, encontrar-se-á uma cadeia de caracteres com o seguinte formato:
param1=val1&MultipleChoiceListField=1&MultipleChoiceListField=3¶m2=val2
se o utilizador tiver selecionado as opções [liste1] e [liste3]. É o atributo [value] das opções selecionadas que é enviado. É porque podem ser enviados vários parâmetros com o mesmo nome que [MultipleChoiceListField] é uma matriz de valores e não um valor simples. Se nenhuma caixa de seleção estiver marcada, o parâmetro [MultipleChoiceListField] estará ausente da cadeia enviada e a propriedade com o mesmo nome do modelo não será inicializada.
Os diferentes campos de introdução de dados apresentados anteriormente irão receber os valores enviados pelo formulário. Também é possível inicializá-los antes de enviar o formulário. Foi isso que se fez 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 estes valores tivessem sido obtidos após o envio do formulário com o código POST, isso significaria que o utilizador:
- 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 introdução;
- linha 5: digitou [secret] como palavra-passe;
- linha 6: digitou [ligne1\nligne2] no campo de introdução 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 partir do princípio de que ocorreu um POST e que pretendemos reenviar o formulário tal como foi preenchido. É precisamente isto que acontece quando se reenvia ao utilizador um formulário com erros. Este é reenviado tal como foi preenchido.
5.6.4. A vista [Formulaire]
A vista [/First/Formulaire.cshtml] apresenta 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 baliza <form> do formulário. Este será enviado através do 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: apresentam os botões de opção:
- linha 22: percorre-se a coleção apresentada pelo botão de opção;
- linha 24: o botão cujo atributo [value] tem o valor da propriedade [RadioButtonField] deve estar selecionado. Para tal, deve ter o atributo [checked="checked"];
- linha 25: geração da baliza <input type="radio"> com o valor [@item.Value] e o texto [@item.Label];
- linha 26: a baliza <text/> não é uma baliza HTML reconhecida. Está presente para [Razor]. Ao encontrá-la, [Razor] irá gerar um salto de linha. Isto não tem impacto no formulário apresentado, mas tem impacto no código HTML gerado. As balizas <input type="radio"> ficam, assim, em duas linhas diferentes, em vez de estarem na mesma linha. Isto torna o código mais legível quando, no navegador, se solicita a visualização do código-fonte da página apresentada;
Analisamos agora os restantes elementos da vista:
<!-- as 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 apresentada pelas caixas de seleção;
- linha 8: uma célula que tenha como atributo [value] um dos valores da propriedade [CheckBoxesField] deve estar assinalada. Para tal, deve ter o atributo [checked="checked"]. Utiliza-se uma expressão LINQ que permite verificar se um valor está contido numa tabela;
- linha 25: geração da baliza <input type="checkbox"> com o valor [@item.Value] e o rótulo [@item.Label];
<!-- o campo de introdução de texto de uma linha -->
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="TextField" value="@Model.TextField" size="30" />
</td>
</tr>
<!-- o campo de introdução de palavra-passe -->
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="PasswordField" value="@Model.PasswordField" size="30" />
</td>
</tr>
<!-- o campo de introdução de texto multilinha -->
<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 baliza 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 apresentada pela lista suspensa;
- linha 9: deve então ser selecionada uma opção cujo atributo [value] tenha o valor da propriedade [DropDownListField]. Para tal, essa opção deve ter o atributo [selected="selected"];
- linha 25: geração da baliza <option value="valeur">libellé</option> com o valor [@item.Value] e o rótulo [@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 apresentada pela lista;
- linha 9: deve ser selecionada uma opção cujo atributo [value] tenha um dos valores da propriedade [MultipleChoiceListField]. Para tal, deve possuir o atributo [selected="selected"]. Utiliza-se uma expressão LINQ que permite determinar se um valor está contido numa matriz;
- linha 10: geração da baliza <option value="valeur">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 num tipo [FormCollection]. O valor do parâmetro [RadioButtonField] enviado é obtido através da expressão posted[" RadioButtonField"]. O resultado é uma cadeia de caracteres ou o ponteiro null. Se escrevermos posted[" CheckBoxesField"], obteremos um tabuleiro 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 irá instanciar o modelo [ViewModel08] com o construtor sem parâmetros, o que terá como efeito não inicializar as coleções do modelo;
- a segunda é que queremos controlar o que vai para o modelo. Sabemos que existem quatro fontes possíveis para o modelo: os parâmetros de um GET, de um POST, da rota utilizada e os de um ficheiro uploadé. Aqui, queremos inicializar o modelo apenas com os valores enviados.
- linha 6: instanciamos o modelo utilizando o construtor correto;
- linha 7: inicializa-se o modelo com os valores enviados. Após esta operação, o modelo corresponde aos dados introduzidos pelo utilizador;
- linha 8: voltamos a apresentar o formulário. O utilizador irá encontrá-lo tal como foi preenchido.
Vejamos um exemplo:
![]() |
No [2], o resultado do [POST] reflete corretamente o que foi introduzido no [1].
5.6.6. Tratamento de anomalias do POST
Já referimos que, se nenhum valor fosse assinalado ou selecionado nos campos [CheckBoxesField, SimpleChoiceListField, MultipleChoiceListField], os parâmetros correspondentes não fariam parte da cadeia enviada e, por conseguinte, as propriedades com os mesmos nomes no modelo não seriam inicializadas.
Vejamos o seguinte exemplo:
![]() |
- em [1], nenhuma caixa de seleção foi marcada;
- em [2], o [POST] devolve uma caixa de seleção 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 forma:
[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. Ora, o construtor utilizado atribui a tabela ["2"] à propriedade [CheckBoxesField];
- linha 6: os valores lançados são registados no modelo. Como o parâmetro [CheckBoxesField] não faz parte dos valores enviados, a propriedade com o mesmo nome não é atribuída. Mantém-se, portanto, com o seu valor ["2"], o que faz com que, na visualização, a caixa n.º 2 esteja marcada quando não deveria estar.
Este problema pode ser resolvido de várias formas. 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. Se não for o caso, inicializam-se com o valor que corresponde à ausência de introdução de dados pelo utilizador. O teste não foi efetuado para a lista suspensa, que tem sempre um elemento selecionado, o que não acontece com as outras listas.
Convidamos o leitor a testar esta nova versão.
5.7. Utilização 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 irá gerar um formulário idêntico ao anterior:
![]() |
Voltemos ao código utilizado 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>
Este 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 nos enganarmos no nome da propriedade do modelo a utilizar como atributo [name], só nos aperceberemos disso na execução.
ASP.NET MVC oferece métodos especializados denominados [HTML Helpers] que, tal como o próprio nome indica, visam facilitar a geração do HTML, nomeadamente para formulários. Com estas classes, a lista suspensa anterior escreve-se 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-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 estas linhas HTML:
@Html.DropDownListFor(m => m.DropDownListField, new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
- o primeiro parâmetro é uma função lambda (este é o seu nome), em que m representa o modelo da vista e m.DropDowListField é uma propriedade desse modelo. O gerador de código HTML utilizará o nome desta 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 na execução. Trata-se de uma melhoria em relação à solução anterior, na qual os erros de nomenclatura só eram detetados na execução;
- o segundo parâmetro serve para designar a coleção de elementos que irá alimentar a lista suspensa. A classe [SelectList] permite construir essa coleção:
- o seu primeiro parâmetro é uma coleção qualquer de elementos. Neste caso, temos uma coleção do tipo [Item];
- o seu segundo parâmetro é a propriedade dos elementos que fornecerá o valor da baliza <option>. Neste caso, trata-se da propriedade [Value] da classe [Item];
- o seu terceiro parâmetro é a propriedade dos elementos que fornecerá o texto da baliza <option>. Aqui, trata-se da propriedade [Label] da classe [Item];
- para saber qual a opção que 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].
Vejamos agora os outros métodos que podemos utilizar:
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 é o [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 atribuir ao botão de opção (atributo [value]).
Caixas de seleção
O código evolui 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 é 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 ficará marcada. Se for [Propriété=false], a caixa não ficará 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 razão existem duas tags [input] com o mesmo nome? Existem dois casos:
- a caixa de seleção da linha 1 está marcada. Nesse caso, a cadeia de parâmetros enviada é Propriedade=true&Propriedade=false (linhas 1 e 2). Como a propriedade [Propriété] espera apenas um valor, pode-se pensar que o framework atribui o valor [true] a [Propriété]. Bastaria fazer 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. Assim, a cadeia de parâmetros enviada é Propriedade=false (apenas na linha 2) e, por isso, a propriedade [Propriété] recebe o valor [false], o que está correto (a caixa de seleção não foi marcada).
Campo de introdução de texto de uma linha
O novo código é o seguinte:
<!-- o campo de introdução de texto de uma linha -->
<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 introdução 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 introdução de dados. O nome da propriedade será utilizado nos atributos [name] e [id] da baliza <input> gerada e o seu valor será atribuído ao atributo [value];
- o segundo parâmetro é uma classe anónima que especifica determinados atributos da baliza HTML gerada, neste caso o atributo [size].
Campo de introdução de uma palavra-passe
O novo código é o seguinte:
<!-- o campo de introdução de uma palavra-passe -->
<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 introdução de uma palavra-passe -->
<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 introdução de texto com várias linhas
O novo código é o seguinte:
<!-- o campo de introdução de texto multilinha -->
<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 introdução 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 é análogo 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 escolha ú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á analisámos o método [Html.DropDownListFor]. A única diferença aqui é o terceiro parâmetro, que serve para especificar um atributo [size] diferente de 1. É esta característica que faz com que se passe de uma lista suspensa [size=1] para uma lista simples.
A lista de escolha múltipla
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 baliza <form> também pode ser gerada com 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 vista apresentada 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 introdução de dados
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 apresentar 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; }
// fabricantes
public ViewModel09()
{
}
public ViewModel09(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";
CheckBoxField2 = true;
TextField = "quelques mots";
PasswordField = "secret";
TextAreaField = "ligne1\nligne2";
DropDownListField = "2";
SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };
}
}
}
O [ViewModel09] difere do [ViewModel08] na forma como gere as caixas de seleção. Em vez de ter uma tabela com três caixas de seleção, foram utilizadas três caixas de seleção separadas (linhas 11-13).
O formulário será processado pela ação [Action09Post] seguinte:
// 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 aspetos:
- linha 18: é utilizada a vista [Formulaire2] em vez da vista [Formulaire];
- já não existe a gestão das caixas de seleção que não foram marcadas. Agora, isso é gerido 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, que permitirão ao framework MVC saber que tag de entrada deve gerar. Estas informações são designadas por metadados.
Consideremos o seguinte modelo de vista [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 constituídos pelas etiquetas [Display, DataType, UIHint].
Este 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 à vista predefinida da ação [/First/Action10Get.cshtml ] que apresente o modelo de vista do tipo [ViewModel10]. Esta vista é 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 apresentar o valor do metadado [DisplayName] da propriedade;
- Html.EditorFor para gerar a baliza HTML de introdução do valor da propriedade. Este método irá utilizar os metadados [DataType] e [UIHint] da propriedade;
- Html.DisplayFor para apresentar o valor da propriedade de acordo com o formato indicado pelos metadados [DataType].
Eis um exemplo de execução com o navegador Chrome:

Dependendo do navegador utilizado, podem ser apresentadas páginas diferentes. Com efeito, a visualização gerada utiliza as novas balizas introduzidas pela versão 5 do HTML, denominada HTML5. Nem todos os navegadores suportam ainda esta versão. No exemplo acima, o navegador Chrome suporta-a parcialmente.
5.8.1. O [POST] do formulário
O [POST] do formulário é processado pela seguinte ação [Action10Post]:
// 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 deste formulário;
- linha 6: prepara-se a resposta de texto para o cliente;
- linha 7: envia-se a resposta.
Vamos agora analisar 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 introdução de dados.
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>
Imagem
![]() |
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 baliza <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 apresentado entre o início e o fim da baliza é o texto dos metadados
[Display(Name="Text")]
O método [Html.LabelFor] procede sempre desta forma. Não voltaremos a abordar este assunto para as outras propriedades.
- O método [Html.EditorFor] gerou a baliza <input> da linha 3. Note-se que esta tem um atributo [class] que associa a classe CSS [text-box single-line] à baliza. Os atributos [id] e [name] têm como valor o nome [Text] da propriedade «parâmetro» do método [Html.EditorFor]. O atributo [type] assumiu o valor [text] devido aos metadados
[DataType(DataType.Text)]
- o método [Html.DisplayFor] gerou o texto da linha 4. Este é o valor da propriedade de parâmetro do método [Html.DisplayFor ]. Este método é influenciado pelos metadados
[DataType(DataType.Text)]
, o que faz com que o valor seja apresentado como texto sem formatação.
5.8.3. Propriedade [MultiLineText]
Definição
[Display(Name = "TextArea")]
[DataType(DataType.MultilineText)]
public string MultiLineText { get; set; }
Vista
<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 baliza <textarea> da linha 3. Note-se que esta possui um atributo [class] que associa a classe CSS [text-box multi-line] à baliza. 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 voltaremos a referir isto. A baliza gerada é <textarea> devido aos metadados
[DataType(DataType.MultilineText)]
, que especificava que a propriedade era um texto multilinha.
- O método [Html.DisplayFor] gerou o texto das linhas 4-5. Este é o valor da propriedade 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 baliza <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. São utilizados por um framework JavaScript de validação de dados do 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 do lado do cliente. Eis dois exemplos:
Introduz-se um número errado e valida-se:
![]() |
No exemplo acima, a validação ocorreu do lado do cliente. O formulário não será enviado enquanto o erro não for corrigido.
Outro exemplo: não se introduz nada:
![]() |
No [1] acima, o [Action10Post] indica um erro. Talvez nos lembremos de que já tínhamos obtido este comportamento ao utilizar o atributo [Required] na propriedade a controlar (ver página 69), neste caso a propriedade [Number]. Aqui, não foi necessário fazê-lo.
5.8.5. Propriedade [Decimal]
Definição
[Display(Name = "Decimal")]
[UIHint("Decimal")]
public double Decimal { get; set; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [text]. Os restantes atributos são idênticos aos gerados para a propriedade [Number] anterior. Os metadados:
[UIHint("Decimal")]
faz com que o valor da propriedade seja apresentado com duas casas decimais nos dois métodos [Html.EditorFor] e [Html.DisplayFor]
Validação
Não é sinalizado qualquer erro de validação do lado do cliente, ao contrário do caso anterior. O erro só é sinalizado pela ação [Action10Post]. Mais uma vez, o número decimal é obrigatório sem que seja necessário atribuir-lhe o atributo [Required].
5.8.6. Propriedade [Tel]
Definição
[Display(Name = "Tel")]
[DataType(DataType.PhoneNumber)]
public string Tel { get; set; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [tel]. Este valor foi gerado devido aos metadados:
[DataType(DataType.PhoneNumber)]
O tipo [tel] para uma baliza <input> é uma novidade em relação ao HTML5. O navegador Chrome tratou-a como uma baliza <input> com o tipo [text].
Validação
Não é sinalizado qualquer erro de validação, nem do lado do cliente nem do lado do servidor. É possível introduzir qualquer valor.
5.8.7. Propriedade [Date]
Definição
[Display(Name = "Date")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [date]. Este 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 reconhece-o e permite introduzir a data através de um calendário. Além disso, a data introduzida é 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
É sinalizada uma data inválida no lado do cliente [1], impedindo o envio do POST do formulário para o servidor.
![]() |
A ausência de data não é sinalizada do lado do cliente, mas é do lado do servidor [2].
5.8.8. Propriedade [Time]
Definição
[Display(Name = "Time")]
[DataType(DataType.Time)]
public DateTime Time { get; set; }
Vista
<tr>
<td>@Html.LabelFor(m => m.Time)</td>
<td>@Html.EditorFor(m => m.Time)</td>
<td>@Html.DisplayFor(m => m.Time)</td>
</tr>
Imagem
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 baliza <input> da linha 3 com um atributo [type] do tipo [time]. Este valor foi gerado devido aos metadados:
[DataType(DataType.Time)]
O tipo [time] para uma baliza <input> é uma novidade em relação ao HTML5. O navegador Chrome reconhece-o e permite introduzir 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 introduzir uma hora inválida. A ausência de hora é sinalizada do lado do servidor:
![]() |
5.8.9. Propriedade [HiddenInput]
Definição
[Display(Name = "HiddenInput")]
[UIHint("HiddenInput")]
public string HiddenInput { get; set; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [hidden], ou seja, um campo oculto (mas, mesmo assim, enviado). Este 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; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [checkbox], ou seja, uma caixa de seleção. Este 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; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [email]. Este tipo é novo no HTML5. Este tipo foi gerado devido aos metadados:
[DataType(DataType.EmailAddress)]
O Chrome parece ter tratado este 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
É sinalizado um endereço inválido no lado do cliente [1]:
![]() |
A ausência de entrada não provoca qualquer erro.
5.8.12. Propriedade [Url]
Definição
[Display(Name = "Url")]
[DataType(DataType.Url)]
public string Url { get; set; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [url]. Este tipo é novo no HTML5. Foi gerado devido aos metadados:
[DataType(DataType.Url)]
O Chrome parece tratar este 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 provoca qualquer erro.
5.8.13. Propriedade [Password]
Definição
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [password]. Este 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; }
Vista
<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 baliza <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. Este formato foi utilizado devido aos metadados:
[DataType(DataType.Currency)]
Validação
Um valor inválido [1] ou a ausência de um valor [2] é sinalizada do lado do servidor:
![]() |
5.8.15. Propriedade [CreditCard]
Definição
[Display(Name = "CreditCard")]
[DataType(DataType.CreditCard)]
public string CreditCard { get; set; }
Vista
<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 baliza <input> da linha 3 com um atributo [type] do tipo [text]. O método [Html.DisplayFor] gerou a linha 4. Não se percebe aqui o que os metadados trazem:
[DataType(DataType.CreditCard)]
Validação
Não é efetuada qualquer verificação, nem do lado do cliente, nem do lado do servidor.
5.9. Validação de um formulário
Já abordámos o problema da validação do modelo de uma ação no parágrafo 4.5 e nos parágrafos seguintes. Voltamos a abordar esta questão no contexto de um formulário:
- como sinalizar os erros de introdução de dados ao utilizador;
- efectuar as validações tanto do lado do cliente como do lado do servidor, de modo a sinalizar mais rapidamente os erros ao utilizador.
5.9.1. Validação do 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" }));
}
// apresenta a lista de erros
return résultats;
}
}
}
Este modelo será apresentado pela seguinte vista [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 referência à folha de estilo [Site.css]. Por predefinição, esta contém classes utilizadas para destacar os erros de preenchimento do formulário;
- linhas 18-25: uma tabela com três colunas:
- a coluna 1 apresenta texto com o método [Html.LabelFor],
- a coluna 2 apresenta os dados introduzidos com o método [Html.EditorFor],
- a coluna 3 apresenta o eventual erro de preenchimento com o método [Html.ValidationMessageFor];
A ação [Action11Get] serve para apresentar o formulário:
// Ação 11 - GET
[HttpGet]
public ViewResult Action11Get()
{
return View("Action11Get", new ViewModel11());
}
A ação [Action11Post] serve para voltar a apresentar o formulário com os eventuais erros de introdução de dados:
// Ação 11-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. Podem então ocorrer erros. A cada propriedade P incorreta do modelo está associada uma mensagem de erro. É essa mensagem que permite obter o método [Html.ValidationMessageFor] do formulário.
Eis um exemplo de execução:
![]() | ![]() |
Eis outro exemplo:
![]() |
Note-se que ambas as datas estão erradas (hoje é 11/10/2013), mas que os erros não são assinalados. Estes erros são detetados 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" }));
}
// apresenta a lista de erros
return résultats;
}
O método [Validate] só é executado quando todas as validações por atributos forem bem-sucedidas. É o que mostra um último exemplo:
![]() |
5.9.2. Validação do lado do cliente
Todas as validações anteriores foram realizadas do lado do servidor. É, portanto, necessária uma troca de dados entre o cliente e o servidor para que o utilizador se aperceba dos seus erros. A validação do lado do cliente utiliza código JavaScript para sinalizar ao utilizador os seus erros o mais cedo possível e, em qualquer caso, antes do POST. Este só pode ocorrer quando todos os erros detetados tiverem sido corrigidos.
Retomamos o modelo [ViewModel11] anterior, mas agora apresentamo-lo com a seguinte vista [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>
Nota: linha 13, adapte a versão de jQuery à que tem com a sua versão do Visual Studio (ver abaixo).
A validação do lado do cliente requer a presença da linha 3 abaixo no ficheiro [Web.config] da aplicação.
<appSettings>
...
<add key="ClientValidationEnabled" value="true" />
</appSettings>
- linhas 1-4: a secção [appSettings] deve ser um elemento filho direto da secção [configuration] do ficheiro [Web.config];
A vista [Action12Get] é idêntica à vista [Action11Get] anterior, com exceção das linhas 13-15. Estas incluem na vista os scripts JavaScript necessários para a validação do lado do cliente. Estes scripts encontram-se na pasta [Scripts] do projeto:
![]() |
Cada script tem uma versão normal ([.js]) e uma versão minimizada ([min.js]). Esta última versão é mais leve, mas ilegível. É utilizada em produção. A versão legível é utilizada em desenvolvimento.
A vista [Action12Get.cshtml] será apresentada pela seguinte ação [Action12Get]:
// Ação 12 - GET
[HttpGet]
public ViewResult Action12Get()
{
return View("Action12Get", new ViewModel11());
}
O formulário preenchido será processado pela seguinte ação [Action12Post]:
// Ação12-POST
[HttpPost]
public ViewResult Action12Post(ViewModel11 modèle)
{
return View("Action12Get", modèle);
}
Vamos ver o que isto altera com um exemplo:
![]() |
Assim que se digita um carácter em [1], a mensagem em [2] é apresentada porque o valor esperado deve ter, pelo menos, quatro caracteres. Assim, a validação é efetuada a cada novo carácter digitado. A mensagem de erro desaparece ao digitar o quarto carácter. Feito isto, vamos validar o formulário:
![]() |
O URL [3] mostra-nos que o [POST] não ocorreu. No entanto, ao clicar no botão [Valider], foram acionadas todas as validações do lado do cliente e surgiram novas mensagens de erro.
Vejamos, por exemplo, o código HTML gerado para o primeiro registo:
<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 de a entrada estar em falta [data-val-required],
- a mensagem de erro no caso de a entrada estar errada [data-val-regex],
- a expressão regular para a cadeia introduzida [data-val-regex-pattern];
- na linha 4, outros atributos [data-x] utilizados para apresentar a eventual mensagem de erro;
Os atributos [data-x] das balizas geradas são utilizados pelo JavaScript que incorporámos na vista. Se este estiver ausente, esses atributos são simplesmente ignorados e, nesse caso, não há validação do lado do cliente. O funcionamento é idêntico ao do exemplo anterior. Daí a designação [unobtrusive] para esta técnica.
5.10. Gestão de links de navegação e de ação
Vamos criar as duas vistas seguintes para ilustrar a gestão de links numa vista:
![]() |
- em [1] e [2], temos dois links de navegação;
- na [3], existe um link de ação que envia o formulário. Este 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á apresentar a vista;
- linhas 23-28: um formulário;
- linha 25: um rótulo para o campo [data];
- linha 26: um campo de introdução de dados denominado [data];
- linha 27: um link do tipo [submit]. Ao clicar nele, é executada a função JavaScript [postForm] (atributo href). Esta função está definida nas linhas 12-17;
- linha 14: obtém-se uma referência ao primeiro formulário do documento, o da linha 23;
- linha 16: este formulário é enviado. No final, tudo acontece como se tivéssemos clicado num botão do tipo [submit]. O formulário é enviado para o controlador e para a 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 estas vistas 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 vista [Action16Get.cshtml], ou seja, a página 1 do exemplo. Esta vista tem como modelo a [ViewBag] (linha 5);
- na linha 19, a ação [Action17Get] gera a vista [Action17Get.cshtml], ou seja, a página 2 do exemplo. Esta vista tem como modelo a [ViewBag] (linha 21);
- linha 11: a ação [Action16Post] processa o POST do formulário da vista [Action16Get.cshtml]. Recebe o parâmetro denominado [data]. Recorde-se que este é o nome do campo de introdução de dados no formulário;
- linha 13: é inserida uma informação no [ViewBag];
- linha 14: a vista [Action16Get.cshtml] é apresentada.
Convidamos o leitor a testar este exemplo.































































