• 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 23/11/2017

Économiser son code avec les helpers et les vues partielles

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Les helpers HTML

Alors, écrire du HTML comme ça, c’est très bien. Mais reconnaissez que c’est un peu fastidieux.
Heureusement, ASP.NET MVC a la solution : les helpers HTML. Ils nous permettent d’écrire moins de code tout en gardant un fort contrôle sur le HTML.

Par exemple, il est très facile de simplifier le code que nous avons vu juste au-dessus :

<div>Je suis la vue index</div>
<input type="text" id="nom" value="@Model.Resto.Nom" />
<input type="text" id="telephone" value="@Model.Resto.Telephone" />
<input type="submit" value="Envoyer" />

en utilisant les helpers HTML ! Voici par exemple l’équivalent avec ces fameux Helpers :

<div>Je suis la vue index</div>
@Html.TextBox("nom", Model.Resto.Nom)
@Html.TextBox("telephone", Model.Resto.Telephone)
<input type="submit" value="Envoyer" />

Voyez comme il est simple de remplacer la syntaxe HTML de l’input de type text. Ce helper va nous générer le HTML suivant :

<input id="nom" name="nom" type="text" value="La bonne fourchette" />
<input id="telephone" name="telephone" type="text" value="1234" />

Ce qui est pile ce qu’on lui a demandé. En fait, c’est même mieux car il a rempli l’attribut id et l’attribut name avec le paramètre que nous lui avons passé. Ainsi, pas de risque d’oublier l’attribut comme nous l’avions fait. Enfin… je dis « nous », mais c’est moi en fait ; je suis sûr que vous ne l’auriez pas oublié :-°.

Ces helpers possèdent beaucoup de surcharges différentes. Ici, j’ai utilisé la surcharge qui me permet de spécifier le nom de la zone de texte et sa valeur. D’autres surcharges me permettent d’agir plus finement sur le HTML généré, par exemple pour rajouter des propriétés supplémentaires, du style…
Ici par exemple, je peux indiquer que le texte de la balise <input> aura pour style une couleur rouge :

@Html.TextBox("nom", Model.Resto.Nom, new { style = "color:red" })

Ce qui me donne le HTML suivant :

<input id="nom" name="nom" style="color:red" type="text" value="La bonne fourchette" />

Mais j’aurai également pu fournir le nom d'une classe CSS :

@Html.TextBox("nom", Model.Resto.Nom, new { style = "color:red", @class = "ma-css" })

Ce qui nous donne :

<input class="macss" id="nom" name="nom" style="color:red" type="text" value="La bonne fourchette" />

On utilise pour ajouter des attributs HTML un objet anonyme dont le nom des propriétés correspond au nom des attributs HTML que nous souhaitons positionner. Ainsi, en créant un objet anonyme avec une propriété s’appelant « style », j’indique au helper que je souhaite qu’il me rajoute l’attribut style de la balise HTML.
Notez que l’attribut class s’obtient en utilisant une propriété anonyme du même nom, mais préfixé par un @ car class  est un mot-clé réservé du C#, utilisé bien sûr pour créer des classes…

Il y a un helper pour à peu près toutes les balises de formulaire. Par exemple, une case à cocher et une zone de texte non éditable peuvent être créées avec les helpers suivants :

<div>Je suis la vue index</div>
@Html.Label("estMajeur", "Cochez la case si vous êtes majeur")
@Html.CheckBox("estMajeur", true)
<input type="submit" value="Envoyer" />

Et nous aurons ce rendu :

Une case à cocher avec le helper Html.CheckBox
Une case à cocher avec le helper Html.CheckBox

Avec le HTML suivant :

<label for="estMajeur">Cochez la case si vous &#234;tes majeur</label>
<input checked="checked" id="estMajeur" name="estMajeur" type="checkbox" value="true" /><input name="estMajeur" type="hidden" value="false" />
<input type="submit" value="Envoyer" />

Notez qu’en bon HTML, l’attribut for du label doit correspondre à l’identifiant de la case à cocher qu’il accompagne afin de permettre sa sélection depuis le texte du label. C'est le cas ici, le premier paramètre du Helper Html.Label  est l’identifiant de la case à cocher.

Les lecteurs assidus auront remarqués que le HTML généré ne correspond pas exactement à ce que nous aurions pu attendre. En effet, il y a un input de type checkbox portant le nom estMajeur mais également un input de type hidden du même nom. Ceci permet de simplifier la récupération de la valeur de la case à cocher côté contrôleur.

Vous aurez également vu dans l’exemple précédent que le helper propose un encodage automatique. Nous y reviendrons un tout petit peu plus loin.

Passons maintenant à deux helpers particuliers qui vont permettre de générer des listes de sélections et qui vont en interne générer la balise HTML <select> . Il s’agit des helpers Html.DropDownList  et Html.ListBox . La particularité de ces helpers est qu’ils doivent être pré-remplis avec une liste de propositions. Pour cela, on utilise un objet SelectList  que l’on va alimenter avec la liste des propositions. Imaginons par exemple que je souhaite avoir une liste déroulante avec tous mes restaurants, je peux construire cette liste ainsi depuis le contrôleur :

List<Models.Resto> listeDesRestos = new List<Resto>
{
    new Resto { Id = 1, Nom = "Resto pinambour", Telephone = "1234" },
    new Resto { Id = 2, Nom = "Resto tologie", Telephone = "1234" },
    new Resto { Id = 5, Nom = "Resto ride", Telephone = "5678" },
    new Resto { Id = 9, Nom = "Resto toro", Telephone = "555" },
};

ViewBag.ListeDesRestos = new SelectList(listeDesRestos, "Id", "Nom");

L’objet SelectList  est construit à partir de la liste des restaurants, sachant qu’on lui passe également le nom de la propriété qui permet d’identifier de manière unique un restaurant, en l’occurrence il s’agit de la propriété Id de la classe Resto  (cette valeur se retrouvera dans la propriété value  de la balise <option>). De même, on lui passe le nom de la propriété qui servira de description de la valeur, ici c’est la propriété Nom  de la classe Resto .
Tout ceci est passé par exemple dans le ViewBag  et sera utilisé dans la vue avec :

@Html.DropDownList("RestoChoisi", (SelectList)ViewBag.ListeDesRestos)

Le premier paramètre du helper correspond classiquement à l’identifiant HTML du contrôle et le deuxième paramètre est la SelectList .

Nous obtenons donc une belle lise déroulante, repliée puis déroulée :

La liste déroulante
La liste déroulante

Et le HTML qui l’accompagne est le suivant :

<select id="RestoChoisi" name="RestoChoisi">
    <option value="1">Resto pinambour</option>
    <option value="2">Resto tologie</option>
    <option value="5">Resto ride</option>
    <option value="9">Resto toro</option>
</select>

Nous voyons bien que les identifiants des restaurants sont dans la propriété value de chaque balise <option>  et que le nom du restaurant correspond au libellé de l’option de sélection.

Vous pourriez trouver étonnant que je vous ai fait construire la liste déroulante à partir du contrôleur et non de la vue. En fait, on peut voir le contrôleur comme préparant le modèle afin d’être affiché par la vue. Il est également possible d’effectuer la construction dans la vue mais en général personne ne le fait car cela alourdit considérablement la vue et la rend beaucoup moins lisible.

Remarquez qu’il est possible de présélectionner un élément de la liste si nous le souhaitons car ici, par défaut il s’est positionné sur le premier. Il suffit dans ce cas d’ajouter un nouveau paramètre au constructeur de la SelectList  pour lui passer l’identifiant de l’élément à sélectionner :

ViewBag.ListeDesRestos = new SelectList(listeDesRestos, "Id", "Nom", 5);

Et ici, lors de l’affichage du contrôle, mon resto ride sera présélectionné :

La liste déroulante avec un élément pré-sélectionné
La liste déroulante avec un élément pré-sélectionné

Le HTML est adapté en conséquence :

<select id="RestoChoisi" name="RestoChoisi">
    <option value="1">Resto pinambour</option>
    <option value="2">Resto tologie</option>
    <option selected="selected" value="5">Resto ride</option>
    <option value="9">Resto toro</option>
</select>

Bon, c’est pareil, je ne vais pas vous passer en revue tous les helper, mais voyons-en encore un qui va vous servir : il s’agit du helper qui permet de générer la balise de formulaire <form> .
La balise de formulaire, et notamment son attribut action , est fortement liée à la route où nous souhaitons envoyer les informations. Rappelez-vous, une balise de formulaire a cette tête :

<form action="/MonControleur/MonAction" method="post">
    ...
</form>

sachant que la méthode peut être soit post, soit get. Le truc, c’est qu’il n’est pas toujours facile de connaître l’URL vers laquelle il faut envoyer les données. Dans notre cas simpliste, si je souhaite envoyer les données à la méthode Index  du contrôleur Home , alors il suffit de saisir l’adresse /Home/Index. Simple. Mais c’est parce que nos règles de routing sont très simples (d’ailleurs, il n’y en a qu’une !). Si jamais cela se complexifie un peu, ou si les définitions des routes changent en cours… de route :-° je ne peux pas garantir que l’URL /Home/Index continuera d’invoquer le bon contrôleur et la bonne méthode. 

Cela serait quand même grandement plus simple si je pouvais lui dire d’envoyer vers une méthode d’un contrôleur que je connais, plutôt que sur une adresse dont je ne sais pas si elle va changer…

Eh bien ASP.NET MVC nous a entendus ! Chouette. En fait, il avait plutôt prédit notre besoin avant que nous l’ayons, mais ça c’est parce qu’il est trop balèze… :p

Pour cela, il suffit d’utiliser le helper du formulaire et c’est lui qui va se charger de construire l’URL à partir d’un nom du contrôleur et d’une de ses méthodes. Le helper en question est Html.BeginForm  et vient avec son acolyte Html.EndForm . On l’utilise ainsi :

<div>Je suis la vue index</div>
@{Html.BeginForm("Index", "Home");}
@Html.Label("estMajeur", "Cochez la case si vous êtes <strong>majeur</strong>")
@Html.CheckBox("estMajeur", true)
<input type="submit" value="Envoyer" />
@{Html.EndForm();}

Il nous génère le HTML suivant :

<form action="/Home" method="post">
    <label for="estMajeur">Cochez la case si vous êtes <strong>majeur</strong></label>
    <input checked="checked" id="estMajeur" name="estMajeur" type="checkbox" value="true" /><input name="estMajeur" type="hidden" value="false" />
    <input type="submit" value="Envoyer" />
</form>

Nous pouvons voir que l’attribut action  de la balise form  est correctement rempli. D’ailleurs, vous voyez qu’ASP.NET a fait ce qu’il voulait, il nous a mis l’action /Home alors qu’il aurait pu nous mettre / ou bien /Home/Index. En fait, il se débrouille avec le système de routing pour mettre une URL valide. Vous pouvez essayer avec d’autres valeurs de méthodes, par exemple :

@{Html.BeginForm("Index2", "Home");}

Et cette fois-ci, il est obligé de nous générer l’URL complète :

<form action="/Home/Index2" method="post">

Ici, l’action par défaut sélectionnée est la méthode post. Nous pouvons la changer grâce à une autre surcharge de la méthode :

@{Html.BeginForm("Index2", "Home", FormMethod.Get);}

Et nous aurons :

<form action="/Home" method="get">

Vous aurez compris que c’est la méthode BeginForm  qui écrit la balise ouvrante <form>  et que c’est la méthode EndForm  qui écrit la balise fermante. Il est possible de simplifier l’écriture grâce au mot clé using . Ainsi, ceci est l’équivalent de ce que nous avons vu précédemment :

<div>Je suis la vue index</div>
@using (Html.BeginForm("Index", "Home"))
{
    @Html.Label("estMajeur", "Cochez la case si vous êtes <strong>majeur</strong>")
    @Html.CheckBox("estMajeur", true)
    <input type="submit" value="Envoyer" />
}

Vous pouvez librement utiliser l’une ou l’autre syntaxe en fonction de ce que vous trouvez le plus clair au moment opportun ; personnellement, je suis fan de la construction avec le using  :).

Remarquez que quasiment tous les helpers ont une surcharge où nous pouvons spécifier des attributs HTML, c’est aussi le cas du helper de formulaire. Pour pouvoir envoyer notre formulaire dans une nouvelle page par exemple, nous pourrions utiliser le code suivant :

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { target = "_blank" }))
{
    ...
}

Ce qui donnera :

<form action="/Home" method="post" target="_blank">

Les helpers fortement typés

Ils fonctionnent comme les helpers que nous venons de voir, mais plutôt que de travailler avec des chaînes de caractères, ils fonctionnent grâce au pouvoir des expressions lambdas.
Nous avons par exemple vu un helper juste au-dessus qui permettait de générer une zone de texte modifiable :

@Html.TextBox("nom", Model.Resto.Nom)

Celui-ci peut également s’écrire :

@Html.TextBoxFor(model => model.Resto.Nom)

Ces helpers sont quasiment équivalent, mais le second bénéficie, grâce aux expressions lambda, du support de la complétion automatique ; ceci permet également de simplifier les éventuels refactoring. Plus besoin de vérifier toutes les chaînes de caractères dans les helper si jamais une propriété du modèle change de nom.
En général, il y a un helper fortement typé pour chaque helper disponible, ils sont simplement suffixés par For.

Ils ne produisent cependant pas exactement le même code HTML. Voici ce que génère le premier :

<input id="nom" name="nom" type="text" value="La bonne fourchette" />

Et le second :

<input data-val="true" data-val-required="Le champ Nom est requis." id="Resto_Nom" name="Resto.Nom" type="text" value="La bonne fourchette" />

En fait c’est quasiment pareil ; outre le nom du champ qui est légèrement différent, on a bien un input de type text possédant la bonne valeur. Il y a cependant des attributs en plus, data-val  et data-val-required . Nous pouvons nous en moquer pour l’instant car ils ne vont pas encore nous servir. Par contre, ils serviront plus tard quand nous découvrirons la validation. C’est en fait à cause de notre modèle. Rappelez-vous, la classe Resto  est la suivante :

[Table("Restos")]
public class Resto
{
    public int Id { get; set; }
    [Required]
    public string Nom { get; set; }
    public string Telephone { get; set; }
}

Vous commencez à vous douter d’où cela vient. Et oui, de l’attribut [Required]  qui se trouve au-dessus de la propriété Nom. Pour rappel, il permet d’indiquer que dans un Resto , le nom est obligatoire. Ce qui a pour effet de rendre le champ de la base de données en « not null  » ; mais vous pouvez voir que cela a un autre effet dans notre vue. Nous y reviendrons quand nous parlerons de validation.
Si nous avions utilisé ce helper avec la propriété Telephone , qui n'a pas d'attributs :

@Html.TextBoxFor(model => model.Resto.Telephone)

Nous aurions eu le simple HTML suivant :

<input id="Resto_Telephone" name="Resto.Telephone" type="text" value="1234" />

Ces attributs décorant les propriétés ont d’autres fonctions. Prenons par exemple le helper LabelFor  qui est en général utilisé concomitamment d’un helper TextBoxFor  pour annoncer sa description :

@Html.LabelFor(model => model.Resto.Telephone)
@Html.TextBoxFor(model => model.Resto.Telephone)

Nous obtenons le rendu suivant :

Le label sans attribut sur la propriété du modèle
Le label sans attribut sur la propriété du modèle

Il a bien rempli le label avec le nom de la propriété, en l’occurrence Telephone . Problème, il manque un accent !
Pour le mettre, il suffit de modifier le modèle en rajoutant un attribut :

[Table("Restos")]
public class Resto
{
    public int Id { get; set; }
    [Required]
    public string Nom { get; set; }
    [Display(Name="Téléphone")]
    public string Telephone { get; set; }
}

Et voilà, la magie opère, nous obtenons notre accent :

Le label avec un attribut changeant son libellé
Le label avec un attribut changeant son libellé

Les helpers sont capables d’aller lire les attributs facultatifs que nous pouvons mettre sur les éléments que nous souhaitons afficher. Ici, ces attributs sont positionnés sur notre modèle, mais cela aurait fonctionné de la même façon sur le view-model.

Enfin, parlons rapidement du helper Html.ActionLink  qui permet de générer un lien entre des pages via la balise <a href…> . L’intérêt de ce helper est qu’il permet, comme le helper Html.BeginForm,  de simplement indiquer un nom de méthode et un nom de contrôleur pour générer correctement une URL. Il se sert évidemment du même mécanisme de routing afin d’avoir toujours une URL cohérente avec nos contrôleurs.
Il est intéressant de noter que nous pouvons passer des paramètres (comme pour BeginForm ) aux actions. Prenons par exemple le contrôleur fictif, car pas encore créé, nous permettant de modifier un restaurant : RestaurantController , ainsi que sa méthode ModifierRestaurant . Nous pourrions générer un lien vers cette action avec le helper suivant :

@Html.ActionLink("Modifier le restaurant", "ModifierRestaurant", "Restaurant")

(Dans le premier paramètre, il s’agit du nom apparaissant dans le lien. Le suivant correspondant au nom de l’action à invoquer et le dernier est le nom du contrôleur à instancier). Ceci nous génère ce HTML :

<a href="/Restaurant/ModifierRestaurant">Modifier le restaurant</a>

Il est possible de passer des paramètres à l’action ModifierRestaurant . En l’occurrence, nous avons besoin d’un identifiant de restaurant pour savoir lequel modifier. Cela se passe ainsi :

@Html.ActionLink("Modifier le restaurant", "ModifierRestaurant", "Restaurant", new { id = Model.Resto.Id }, null)

Ce qui nous génère le HTML suivant :

<a href="/Restaurant/ModifierRestaurant/3">Modifier le restaurant</a>

En imaginant que l’id du restaurant valle 3, bien sûr.
Notez qu’il est important de passer le dernier paramètre à null (qui correspond en fait aux éventuels attributs HTML à passer, comme le style) afin d’utiliser la bonne surcharge du helper.

Remarque : si vous générez un lien vers une action d’un même contrôleur, il n’est pas obligatoire de le préciser dans le helper, ceci est automatique. Nous pourrons remplacer par exemple :

@Html.ActionLink("Modifier le restaurant", "ModifierRestaurant", "Restaurant")

Par :

@Html.ActionLink("Modifier le restaurant", "ModifierRestaurant")

à partir du moment où l’action est située dans le même contrôleur.

Nous verrons dans le chapitre sur les contrôleurs comment récupérer ces paramètres, même si vous devez déjà vous en douter un peu. :)

Je ne peux pas décrire tous les helpers, ce serait bien ennuyeux et fastidieux. N’hésitez pas à regarder un peu sur Internet comment ils fonctionnent. En voici une liste :

Helper

Description

Html.ActionLink 

Génère un lien vers une méthode (action) de contrôleur avec la balise <a href> 

Html.BeginForm 

Génère une balise de formulaire <form> 

Html.CheckBox 

Génère une case à cocher <input>  de type "checkbox "

Html.DropDownList 

Génère une liste déroulante de type <select>, <option> 

Html.Hidden 

Génère un champ caché <input>  de type "hidden "

Html.ListBox 

Génère une liste déroulante multi-sélectionnable de type <select> , <option> 

Html.Password 

Génère une zone de texte permettant de saisir un mot de passe <input>  de type "password "

Html.RadioButton 

Génère un bouton radio avec une balise <input>  de type "radio "

Html.RouteLink 

Fait la même chose que Html.ActionLink  à part qu’il utilise une route en entrée

Html.TextArea 

Génère une zone de texte modifiable sur plusieurs lignes avec la balise <textarea> 

Html.TextBox 

Génère une zone de texte modifiable sur une seule ligne avec la balise <input>  de type "text "

Remarquez qu’il existe également d’autres helpers que les helper HTML. Il y a les helpers URL, qui grosso modo font la même chose que le helper ActionLink  à ceci près qu’ils ne génèrent pas la balise <a>  mais seulement le lien correct en utilisant le mécanisme de routing.
En voici la liste :

Helper

Description

Url.Action 

Génère une URL en utilisant le mécanisme de routing

Url.Content

Génère une URL absolue à partir d’une URL relative

Url.RouteUrl

Fait la même chose que Url.Action  à part qu’il utilise une route en entrée

De même, il existe des helpers Ajax que nous utiliserons dans le chapitre dédié.

Je ne vous ai pas présenté tous ces helpers en détails, mais rassurez-vous, nous en verrons quelques-uns en action dans les chapitres ultérieurs.

Vaut-il mieux utiliser les helpers HTML ou bien écrire directement le HTML en le mixant avec du C# ?

C’est une question importante qu’il faut se poser. Globalement, il est plus sage d’utiliser les helpers car ils génèrent un code bien formé. Ils peuvent également générer plein d’attributs supplémentaires qui vont nous servir pour la validation, que je présenterai un peu plus loin. Cependant, si tous ces attributs ne sont pas nécessaires, vous pouvez utiliser votre propre écriture afin de réduire un peu la verbosité du HTML. Ce dernier cas est quand même rare ; globalement préférez l’utilisation des helpers.

Les vues partielles

Si vous avez besoin de réutiliser des morceaux de vues à plusieurs endroits, alors les vues partielles sont pour vous. Imaginons par exemple que nous ayons besoin d’un formulaire de connexion dans notre site. Nous pourrions imaginer faire une vue qui possède un formulaire avec des labels, des textbox et un bouton de soumission. Le tout bien joli avec du CSS et tout ce qui va bien. Mais si nous devons poser ce formulaire sur d’autres pages du site, nous allons devoir refaire à nouveau ce même formulaire, vive le copier-coller…

La solution : la vue partielle.

Les vues partielles viennent avec un helper qui permet de renvoyer une vue sous la forme d’une chaîne de caractères. Cette chaîne peut ainsi être utilisée à l’intérieur d’une autre vue pour produire du HTML qui se répète.

Créons pour l’exemple une nouvelle vue dans le sous-répertoire Accueil, avec notre bouton droit préféré, ajouter -> vue. Dans la fenêtre qui s’ouvre, nous avons une case à cocher nous permettant de créer une vue partielle :

Ajouter une vue partielle
Ajouter une vue partielle

Appelons-la Connexion. Visual Studio nous génère une vue mais cette fois-ci elle est vide. En tout cas, il n’y a pas les balises de base d’une page HTML. En effet, il ne s’agit pas d’une page, mais d’un bout de page. Du coup, elle ne doit surtout pas contenir les balises <html>  ou encore <body>

Pour l’exemple, nous n’allons pas faire une vue partielle trop complexe. Elle sera simplement :

@using (Html.BeginForm())
{
    <fieldset>
        <legend>Connexion :</legend>
        @Html.Label("Login")
        @Html.TextBox("login")
        <br />

        @Html.Label("Mot de passe")
        @Html.TextBox("mdp")
        <br />
        <input type="submit" value="Se connecter..." />
    </fieldset>
}

Et pour l’utiliser ? C'est très facile. Il suffit d’utiliser le helper Html.Partial qui renvoie une chaîne de caractères représentant du HTML. Notre vue Index sera :

<div>Je suis la vue index</div>
@Html.Partial("Connexion")

Et si l’on souhaite inclure notre contrôle de connexion dans une autre page ? Il suffit de cette simple ligne et c’est gagné !
Pratique non ?

En fait, ce n’est pas tout à fait vrai. :p Disons que c’est vrai si la vue partielle est dans le même répertoire, mais il faudra donner le chemin d’accès si jamais elle se trouve dans un autre répertoire.
Par exemple, imaginons que je crée une vue Index dans le répertoire Views\Test (avec son contrôleur classique). Pour afficher ma vue partielle connexion, je devrais faire :

<div>Je suis la vue index de test</div>
@Html.Partial("../Accueil/Connexion")

Ou plus simplement :

<div>Je suis la vue index de test</div>
@Html.Partial("~/Views/Accueil/Connexion.cshtml")

Notez l’utilisation de l’extension de fichier dans la deuxième solution. Et nous aurons :

Affichage de la vue partielle
Affichage de la vue partielle

Bien sûr, il est plutôt rare d’afficher juste du HTML comme ceci. En général, nous aurons besoin de pouvoir passer des paramètres à cette vue partielle, ici pourquoi pas une chaîne de caractères pour pré-remplir le champ de login.
En fait, nous pouvons passer n’importe quel objet en paramètre. Pour illustrer ceci, je vais passer complètement notre view-model. Il suffit d’utiliser une autre surcharge de Html.Partial .
Première chose, je rajoute une propriété Login  à mon view-model :

public class AccueilViewModel
{
    [Display(Name = "Le message")]
    public string Message { get; set; }
    public DateTime Date { get; set; }
    public Models.Resto Resto { get; set; }
    public string Login { get; set; }
}

Puis, dans la vue Index , je n’ai plus qu’à transformer l’appel du helper :

<div>Je suis la vue index</div>
@Html.Partial("Connexion", Model)

Et enfin, dans la vue partielle Connexion , je dois typer fortement la vue :

@model ChoixResto.ViewModels.AccueilViewModel

Puis utiliser la propriété Login  du view-model depuis la vue partielle :

@Html.TextBox("login", Model.Login)

Et voilà, maintenant en affichant la vue, mon TextBox  sera pré-rempli.

Affichage de la vue partielle utilisant le modèle
Affichage de la vue partielle utilisant le modèle

Toujours dans les vues partielles, il y a un autre helper qui permet de renvoyer une petite portion d’une page. Il s’agit du helper Html.Action  (ou son acolyte Html.RenderAction ) qui offre un contrôle plus fin et une meilleure réutilisabilité. En effet, Html.Action  invoque en fait un contrôleur qui renvoie une vue partielle. Ceci offre la possibilité à la page partielle de charger elle-même son modèle dans un contexte différent.
Créons par exemple une action dans le contrôleur Accueil  qui permet de renvoyer une liste de restaurants :

public ActionResult AfficheListeRestaurant()
{
    List<Models.Resto> listeDesRestos = new List<Resto>
    {
        new Resto { Id = 1, Nom = "Resto pinambour", Telephone = "1234" },
        new Resto { Id = 2, Nom = "Resto tologie", Telephone = "1234" },
        new Resto { Id = 5, Nom = "Resto ride", Telephone = "5678" },
        new Resto { Id = 9, Nom = "Resto toro", Telephone = "555" },
    };
    return PartialView(listeDesRestos);
}

Notez la présence de la méthode PartialView  qui remplace l’appel de la méthode View() .

Puis créez la vue AfficheListeRestaurant, partielle bien sûr, et typez-la avec une liste de restaurants :

@model List<ChoixResto.Models.Resto>

<table>
    <tr>
        <th>Nom</th>
        <th>Téléphone</th>
    </tr>
    @foreach (var resto in Model)
    {
    <tr>
        <td>@resto.Nom</td>
        <td>@resto.Telephone</td>
    </tr>
    }
</table>

Maintenant, il ne reste plus qu’à modifier la vue principale pour inclure la vue partielle :

<div>Je suis la vue index</div>
@Html.Action("AfficheListeRestaurant")

On lui passe en paramètre le nom de l’action du contrôleur que l’on souhaite invoquer. Bien sûr, il est possible d’utiliser un autre contrôleur en utilisant les autres surcharges du helper Html.Action .
La vue principale affiche sa vue, rencontre le helper Html.Action , instancie l’action demandée ; le contrôleur renvoie la vue partielle qui s’affiche dans la vue principale.

Et voilà, notre vue partielle s’affiche dans notre vue Index :

Affichage d'une vue partielle à partir d'une action
Affichage d'une vue partielle à partir d'une action

Vous voyez tout de suite la puissance de ce helper… Par contre, vous voyez peut-être son inconvénient… Et oui, nous avons créé une nouvelle action dans notre contrôleur. Donc, en toute logique, on peut l’invoquer directement via l’URL http://localhost:60818/Accueil/AfficheListeRestaurant. Essayons :

Affichage direct de la vue associée à l'action
Affichage direct de la vue associée à l'action

Diantre. Ça fonctionne. En plus, le HTML ne va pas du tout, on peut le voir au titre de la page… Il n’y en a pas. Si vous regardez le code source de la page, vous pouvez voir qu’elle n’est composée que du code de la vue partielle :

<table>
    <tr>
        <th>Nom</th>
        <th>Téléphone</th>
    </tr>
    <tr>
        <td>Resto pinambour</td>
        <td>1234</td>
    </tr>
    <tr>
        <td>Resto tologie</td>
        <td>1234</td>
    </tr>
    <tr>
        <td>Resto ride</td>
        <td>5678</td>
    </tr>
    <tr>
        <td>Resto toro</td>
        <td>555</td>
    </tr>
</table>

Pas de balise <html>, <body> , rien… Ce qui ne va pas du tout.
Non seulement on peut appeler cette vue alors que je ne veux pas forcément qu’elle soit affichable sans sa vue mère, mais en plus elle génère un mauvais HTML.
Heureusement, ASP.NET MVC sait gérer ce genre de situation. Il vaut mieux, sinon il serait un peu ridicule comme framework… On peut marquer la méthode de notre contrôleur comme n’étant appelable qu’à partir d’une vue mère, c’est-à-dire qu’en tant que vue fille. Il suffit d’utiliser un attribut :

[ChildActionOnly]
public ActionResult AfficheListeRestaurant()
{
    List<Models.Resto> listeDesRestos = new List<Resto>
    {
        new Resto { Id = 1, Nom = "Resto pinambour", Telephone = "1234" },
        new Resto { Id = 2, Nom = "Resto tologie", Telephone = "1234" },
        new Resto { Id = 5, Nom = "Resto ride", Telephone = "5678" },
        new Resto { Id = 9, Nom = "Resto toro", Telephone = "555" },
    };
    return PartialView(listeDesRestos);
}

Il s’agit de l’attribut ChildActionOnlyAttribute .
Et hop, maintenant lorsque l’on tente d’invoquer directement l’action, nous obtenons une erreur :

Erreur lors de l'affichage de la vue fille
Erreur lors de l'affichage de la vue fille

Ce qui est bien le but recherché, que l’action ne soit accessible qu’uniquement par une demande enfant.

Une dernière petite chose avec les vues partielles… Si vous êtes curieux, vous avez peut-être testé de remplacer PartialView  par View  et vous vous êtes rendus comptes que ça ne changeait rien. View()  peut très bien faire afficher une vue normale, mais également une vue partielle. Et l’inverse est vrai, PartialView()  peut très bien faire afficher une vue normale comme une vue partielle. Et inversement proportionnel !

En fait, c’est le moteur de vue qui décide comment il doit traiter les vues partielles et les vues normales. Ici, le résultat est le même, mais ça pourrait ne pas être forcément toujours le cas...

En tout cas, ce n'est pas une bonne idée de compter dessus. De toute façon, c’est une bonne chose que d’utiliser les vues partielles et les méthodes qui y sont associées quand c’est vraiment le cas. Déjà, c’est sémantiquement correct et en plus cela permettra aux autres développeurs (ou à vous plus tard) de vraiment voir ce qui est partiel, donc réutilisable, de ce qui est « normal ».

Encodage et sécurité

Si vous vous rappelez un tout petit peu plus haut, nous avons vu que les helpers suivants :

<div>Je suis la vue index</div>
@Html.Label("estMajeur", "Cochez la case si vous êtes majeur")
@Html.CheckBox("estMajeur", true)
<input type="submit" value="Envoyer" />

produisaient le HTML suivant :

<label for="estMajeur">Cochez la case si vous &#234;tes majeur</label>
<input checked="checked" id="estMajeur" name="estMajeur" type="checkbox" value="true" /><input name="estMajeur" type="hidden" value="false" />
<input type="submit" value="Envoyer" />

Et plus particulièrement que le ê de êtes avait été encodé en bon HTML. En fait, ceci fait partie d'un mécanisme d'encodage plus général que tout bon développeur HTML se doit de maîtriser.

Par exemple, que donnerait le code suivant ?

@Html.Label("estMajeur", "Cochez la case si vous êtes <strong>majeur</strong>")

Peut-être espérerait-on que le mot majeur se mettrait en gras grâce à la balise HTML <strong>  ? Que nenni, les caractères < et > sont encodés et s'affichent ainsi en HTML :

<label for="estMajeur">Cochez la case si vous &#234;tes &lt;strong&gt;majeur&lt;/strong&gt;</label>

Et c'est tant mieux. Car ici, tout va bien car c'est moi qui écrit directement le code de ma vue (et j'ai confiance en mon code !), mais que se passerait-il si j'affichais directement le résultat d'une saisie d'un utilisateur ou d'une information venant d'un service web ou d'une base de données que je ne maîtrise pas ? Et bien, vous vous doutez du résultat... plus aucune maîtrise du rendu de la page web, ceci impliquant au passage la possibilité de rendre votre site vulnérable à une attaque.

Et ça, c'est vraiment la dernière des choses que nous voulons !

Heureusement, ASP.NET MVC a pensé à nous et son moteur de vue Razor encode automatiquement tout ce qui est affiché par défaut. Prenez le contrôleur suivant :

public ActionResult Index()
{
    AccueilViewModel vm = new AccueilViewModel
    {
        Message = "Bonjour depuis le <span style=\"color:red\">contrôleur</span>"
    };
    return View(vm);
}

Le fait d'afficher le message dans la vue grâce à :

<div>
  @Model.Message
</div>

produira le HTML suivant :

<div>
    Bonjour depuis le &lt;span style=&quot;color:red&quot;&gt;contr&#244;leur&lt;/span&gt;
</div>

Ainsi, il n'est pas possible d'afficher le mot contrôleur en rouge, comme nous pouvions être tenté de l'imaginer. Et c'est finalement une bonne chose, car nous avons d'autres solutions pour le faire. Ceci nous prémunit d'une grande partie des failles de sécurité.

Cependant, il y a quand même une possibilité pour contourner cet encodage automatique. Imaginons que nous souhaitions afficher directement du HTML et que nous soyons sûrs et certains de sa provenance ou de son format. Cela peut être très pratique de stocker du HTML directement en base de données. Ainsi, si vous utilisez un outil permettant de générer du HTML pour votre bannière ou tout simplement un CMS, le HTML est stocké en base de données et on peut l’afficher directement lors du rendu de la vue. Simple et efficace.

Pour ce faire, on pourra utiliser le helper Html.Raw  :

<div>Je suis la vue index</div>
<ul>
    <li>@Html.Raw(Model.Message)</li>
    <li>@Model.Message</li>
</ul>

et nous aurons le résultat suivant :

Le HTML avec encodage et sans
Le HTML avec encodage et sans

Plutôt pratique ;)

Mais attention, n'oubliez pas qu'afficher du HTML peut rendre votre site vulnérable à une attaque, surtout si ce que vous souhaitez afficher dépend d’une saisie de l’utilisateur.

Nous reviendrons sur ces histoires de sécurité un peu plus loin quand nous aurons appris à traiter les données saisies par l’utilisateur. Vous pouvez cependant retenir ceci :

En résumé

  • Les vues permettent d’afficher le HTML que l’utilisateur verra dans son navigateur.

  • On peut mettre mixer du HTML et du C# dans une vue grâce au caractère @.

  • Il est possible de typer une vue afin que sa propriété Model soit connue et que la vue bénéficie de l’auto-complétion.

  • Il existe un grand nombre de helper HTML qui ont pour objectif de simplifier pour nous la génération des balises des contrôles HTML

  • Les vues peuvent être également partielles afin qu’un bout de page puisse être affiché dans une autre

  • ASP.NET MVC encode automatiquement les caractères pour limiter le risque de failles de sécurité

Exemple de certificat de réussite
Exemple de certificat de réussite