• Aucun résultat trouvé

5.7 Utilisation de méthodes spécialisées dans la génération de formulaire 5.7.1 Le nouveau formulaire

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].