Revenons sur le code utilisé pour générer la liste déroulante du formulaire :
1. <!-- la liste déroulante -->
2. <tr>
3. <td>Liste déroulante</td> 4. <td>
5. <select name="DropDownListField"> 6. @{
7. foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems) 8. {
9. string strChecked = item.Value == @Model.DropDownListField ?
"selected=\"selected\"" : "";
10. <option value="@item.Value" @strChecked>@item.Label</option> 11. }
12. } 13. </select> 14. </tr>
Ce code présente deux inconvénients :
• le plus important est qu'on perd de vue la nature du composant, ici une liste déroulante, à cause de la complexité du code ; • ligne 5 : si on se trompe dans le nom de la propriété du modèle à utiliser comme attribut [name], on ne s'en apercevra qu'à
l'exécution.
ASP.NET MVC offre des méthodes spécialisées appelées [HTML Helpers] qui, comme leur nom l'indique, vise à faciliter la génération du HTML, notamment pour les formulaires. Avec ces classes, la liste déroulante précédente s'écrit comme suit :
1. <!-- la liste déroulante -->
2. <tr>
3. <td>Liste déroulante</td>
4. <td>@Html.DropDownListFor(m => m.DropDownListField,
5. new SelectList(@Model.DropDownListFieldItems, "Value", "Label")) 6. </td>
7. </tr>
La liste déroulante est générée par les lignes 4-5. Le code est nettement moins complexe. Le code HTML généré pour la liste déroulante est le suivant :
1. <!-- la liste déroulante --> 2. <tr>
3. <td>Liste déroulante</td>
4. <td><select id="DropDownListField" name="DropDownListField"><option
value="1">choix1</option>
5. <option selected="selected" value="2">choix2</option>
6. <option value="3">choix3</option>
7. </select></td>
8. </tr>
• ligne 4 : l'attribut [name] est correct ;
Revenons au code qui a généré ces lignes HTML :
@Html.DropDownListFor(m => m.DropDownListField, new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
• le premier paramètre est une fonction lambda (c'est son nom) où m représente le modèle de la vue et m.DropDowListField est une propriété de ce modèle. Le générateur de code HTML va utiliser le nom de cette propriété pour générer les attributs [id] et [name] du [select] qui va être généré. Si on utilise une propriété inexistante, on aura une erreur à la compilation et non plus à l'exécution. C'est une amélioration vis à vis de la solution précédente où les erreurs de nommage n'étaient détectées qu'à l'exécution ;
• le second paramètre sert à désigner la collection d'éléments qui va alimenter la liste déroulante. La classe [SelectList] permet de construire cette collection :
• son premier paramètre est une collection quelconque d'éléments. Ici on a une collection de type [Item] ; • son second paramètre est la propriété des éléments qui fournira la valeur de la balise <option>. Ici, c'est la propriété [Value] de la classe [Item] ;
• son troisième paramètre est la propriété des éléments qui fournira le libellé de la balise <option>. Ici, c'est la propriété [Label] de la classe [Item] ;
• pour savoir quelle option doit être sélectionnée (attribut selected), le framework fait comme nous : il compare la valeur de l'option à la valeur actuelle de la propriété [DropDownListField].
Voyons maintenant les autres méthodes que nous pouvons utiliser : Boutons radio
Le nouveau code est le suivant :
1. <!-- les boutons radio -->
2. <tr>
3. <td>Etes-vous marié(e)</td> 4. <td>
5. @{
6. foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems) 7. {
8. @Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)@item.Label 9. <text/>
10. }
11. } 12. </td> 13. </tr>
Le code HTML généré est le suivant :
1. <!-- les boutons radio --> 2. <tr>
3. <td>Etes-vous marié(e)</td>
4. <td>
5. <input id="RadioButtonField" name="RadioButtonField" type="radio" value="1" />oui 6. <input checked="checked" id="RadioButtonField" name="RadioButtonField" type="radio"
value="2" />non 7. </td>
8. </tr>
La méthode utilisée est [Html.RadioButtonFor] :
@Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)
• le premier paramètre est la propriété du modèle qui va être associée au bouton radio (attribut [name]) ; • le second paramètre est la valeur à attribuer au bouton radio (attribut [value]).
Cases à cocher
Le code évolue de la façon suivante :
2. <tr> 3. <td>Cases à cocher</td> 4. <td> 5. @{ 6. @Html.CheckBoxFor(m=>m.CheckBoxField1) @Model.CheckBoxesFieldItems[0].Label 7. @Html.CheckBoxFor(m=>m.CheckBoxField2) @Model.CheckBoxesFieldItems[1].Label 8. @Html.CheckBoxFor(m=>m.CheckBoxField3) @Model.CheckBoxesFieldItems[2].Label 9. } 10.</td>
La méthode utilisée pour générer des cases à cocher est [Html.CheckBoxFor] :
Html.CheckBoxFor(m=>m.Propriété)
La paramètre est la propriété booléenne du modèle qui sera associée à la case à cocher. Si [Propriété=true], la case sera cochée. Si [Propriété=false], la case ne sera pas cochée. Dans tous les cas, l'attribut [value] vaut true. Le code HTML généré est le suivant :
1. <input id="Propriété" name="Propriété" type="checkbox" value="true" />
2. <input name="Propriété" type="hidden" value="false" />
• la case à cocher avec l'attribut [value="true"] ;
• un champ caché (type=hidden) de même nom [Propriété] que la case à cocher avec l'attribut [value="false"]. Pourquoi deux balises [input] de même nom ? Il y a deux cas :
• la case de la ligne 1 est cochée. Alors la chaîne de paramètres postée est Propriété=true&Propriété=false (lignes 1 et 2). Comme la propriété [Propriété] n'attend qu'une valeur, on peut penser que le framework affecte la valeur [true] à [Propriété]. Il lui suffirait de faire un OU logique entre les valeurs reçues pour y arriver ;
• la case de la ligne 1 n'est pas cochée. Alors la chaîne de paramètres postée est Propriété=false (ligne 2 uniquement) et alors la propriété [Propriété] reçoit la valeur [false], ce qui est correct (la case n'a pas été cochée).
Champ de saisie monoligne Le nouveau code est le suivant :
1. <!-- le champ de saisie texte monoligne -->
2. <tr>
3. <td>Champ de saisie</td> 4. <td>
5. @Html.TextBoxFor(m => m.TextField, new { size = "30" }) 6. </td>
7. </tr>
Le code HTML généré est le suivant :
1. <!-- le champ de saisie texte monoligne --> 2. <tr>
3. <td>Champ de saisie</td>
4. <td>
5. <input id="TextField" name="TextField" size="30" type="text"
value="quelques mots" />
6. </td>
7. </tr>
La méthode utilisée est la suivante :
@Html.TextBoxFor(m => m.TextField, new { size = "30" })
• le premier paramètre précise la propriété du modèle associé au champ de saisie. Le nom de la propriété sera utilisé dans les attributs [name] et [id] de la balise <input> générée et sa valeur sera affectée à l'attribut [value] ;
• le second paramètre est une classe anonyme précisant certains attributs de la balise HTML générée, ici l'attribut [size]. Champ de saisie d'un mot de passe
1. <!-- le champ de saisie d'un mot de passe -->
2. <tr>
3. <td>Mot de passe</td> 4. <td>
5. @Html.PasswordFor(m => m.PasswordField, new { size = "15" }) 6. </td>
7. </tr>
Le code HTML généré est le suivant :
1. <!-- le champ de saisie d'un mot de passe --> 2. <tr>
3. <td>Mot de passe</td>
4. <td>
5. <input id="PasswordField" name="PasswordField" size="15" type="password" />
6. </td>
7. </tr>
La méthode utilisée est la suivante :
@Html.PasswordFor(m => m.PasswordField, new { size = "15" })
Le fonctionnement est analogue à celui de la méthode [Html.TexBoxFor]. Champ de saisie multi-ligne
Le nouveau code est le suivant :
1. <!-- le champ de saisie texte multilignes -->
2. <tr>
3. <td>Boîte de saisie</td> 4. <td>
5. @Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows =
"5" })
6. </td> 7. </tr>
Le code HTML généré est le suivant :
1. <!-- le champ de saisie texte multilignes --> 2. <tr>
3. <td>Boîte de saisie</td>
4. <td>
5. <textarea cols="30" id="TextAreaField" name="TextAreaField" rows="5">
6. ligne1
7. ligne2</textarea>
8. </td>
9. </tr>
La méthode utilisée est la suivante :
@Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
Le fonctionnement est analogue à celui de la méthode [Html.TexBoxFor]. Liste à choix unique
Le nouveau code est le suivant :
1. <!-- la liste à choix unique -->
2. <tr>
4. <td>
5. @Html.DropDownListFor(m => m.SimpleChoiceListField, new
SelectList(@Model.SimpleChoiceListFieldItems, "Value", "Label"), new { size = "3" }) 6. </tr>
et le code HTML généré le suivant :
1. <!-- la liste à choix unique --> 2. <tr>
3. <td>Liste à choix unique</td>
4. <td>
5. <select id="SimpleChoiceListField" name="SimpleChoiceListField" size="3">
6. <option value="1">liste1</option>
7. <option value="2">liste2</option>
8. <option selected="selected" value="3">liste3</option>
9. <option value="4">liste4</option>
10. <option value="5">liste5</option>
11. </select>
12. </tr>
Nous avons déjà étudié la méthode [Html.DropDownListFor]. La seule différence est ici le troisième paramètre qui sert à préciser un attribut [size] différent de 1. C'est cette caractéristique qui fait passer d'une liste déroulante [size=1] à une liste simple.
La liste à choix multiple Le nouveau code est le suivant :
1. <!-- la liste à choix multiple -->
2. <tr>
3. <td>Liste à choix multiple</td> 4. <td>
5. @Html.ListBoxFor(m => m.MultipleChoiceListField, new
SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" }) 6. </tr>
et le code HTML généré le suivant :
1. <!-- la liste à choix multiple --> 2. <tr>
3. <td>Liste à choix multiple</td>
4. <td>
5. <select id="MultipleChoiceListField" multiple="multiple"
name="MultipleChoiceListField" size="5">
6. <option selected="selected" value="1">liste1</option>
7. <option value="2">liste2</option>
8. <option selected="selected" value="3">liste3</option>
9. <option value="4">liste4</option>
10. <option value="5">liste5</option>
11. </select>
12. </tr>
La méthode
@Html.ListBoxFor(m => m.MultipleChoiceListField, new
SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
fonctionne comme la méthode [Html.DropDownListFor] si ce n'est qu'elle génère une liste à sélection multiple. Les options sélectionnées sont celles qui ont leur valeur (attribut value) dans le tableau [MultipleChoiceListField].
La balise <form> peut être elle aussi, générée avec une méthode :
1. @using (Html.BeginForm("Action09Post", "First")) 2. {
3. ... 4. }
Le code HTML généré est le suivant :
1. <form action="/First/Action09Post" method="post"> 2. ...
3. </form>
La méthode
Html.BeginForm("Action09Post", "First")
a pour premier paramètre le nom d'une action et pour second paramètre le nom d'un contrôleur.
5.7.2 Les actions et le modèle
Le formulaire sera délivré par l'action [Action09Get] suivante :
1. // Action09-GET
2. [HttpGet]
3. public ViewResult Action09Get(ApplicationModel application) 4. {
5. ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
6. return View("Formulaire2", new ViewModel09(application)); 7. }
La vue délivrée ligne 6 est [Formulaire2] associée au modèle [ViewModel09] suivant :
1. using System.ComponentModel.DataAnnotations; 2. using System.Web.Mvc; 3. using Exemple_03.Models; 4. 5. namespace Exemple_03.Models 6. {
7. public class ViewModel09
8. {
9. // les champs de saisie
10. public string RadioButtonField { get; set; } 11. public bool CheckBoxField1 { get; set; } 12. public bool CheckBoxField2 { get; set; } 13. public bool CheckBoxField3 { get; set; } 14. public string TextField { get; set; } 15. public string PasswordField { get; set; } 16. public string TextAreaField { get; set; } 17. public string DropDownListField { get; set; } 18. public string SimpleChoiceListField { get; set; } 19. public string[] MultipleChoiceListField { get; set; } 20.
21. // les collections à afficher dans le formulaire
22. public ApplicationModel.Item[] RadioButtonFieldItems { get; set; } 23. public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; } 24. public ApplicationModel.Item[] DropDownListFieldItems { get; set; } 25. public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; } 26. public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; } 27. 28. // constructeurs 29. public ViewModel09() 30. { 31. } 32.
34. { 35. // initialisation collections 36. RadioButtonFieldItems = application.RadioButtonFieldItems; 37. CheckBoxesFieldItems = application.CheckBoxesFieldItems; 38. DropDownListFieldItems = application.DropDownListFieldItems; 39. SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems; 40. MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems; 41. // initialisation champs 42. RadioButtonField = "2"; 43. CheckBoxField2 = true; 44. TextField = "quelques mots"; 45. PasswordField = "secret";
46. TextAreaField = "ligne1\nligne2"; 47. DropDownListField = "2";
48. SimpleChoiceListField = "3";
49. MultipleChoiceListField = new string[] { "1", "3" }; 50. }
51. } 52. }
[ViewModel09] différe de [ViewModel08] par sa gestion des cases à cocher. Plutôt que d'avoir un tableau de trois cases à cocher, on a utilisé trois cases à cocher séparées (lignes 11-13).
Le formulaire sera traité par l'action [Action09Post] suivante :
1. // Action09-POST
2. [HttpPost]
3. public ViewResult Action09Post(ApplicationModel application, FormCollection posted) 4. {
5. ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]); 6. ViewModel09 modèle = new ViewModel09(application); 7. TryUpdateModel(modèle, posted);
8. // traitement des valeurs non postées
9. if (posted["SimpleChoiceListField"] == null) 10. {
11. modèle.SimpleChoiceListField = ""; 12. }
13. if (posted["MultipleChoiceListField"] == null) 14. {
15. modèle.MultipleChoiceListField = new string[] { }; 16. }
17. // affichage formulaire
18. return View("Formulaire2", modèle); 19. }
L'action [Action09Post] est identique à l'action [Action08Post] sauf sur deux points : • ligne 18 : la vue [Formulaire2] est utilisée à la place de la vue [Formulaire] ;
• on n'a plus la gestion des cases à cocher non cochées. C'est désormais géré correctement par la méthode [Html.CheckBoxFor].