V. Les Layouts▲
Razor apporte, avec les layouts, une alternative au concept des Masterpages en ASP.NET, qui ne sont pas supportées par Razor.
Pour ceux qui n'ont pas l'habitude d'ASP.NET, ou qui n'ont jamais utilisé les Masterpages, le principe est de regrouper, dans une page spécifique, les éléments communs de mise en page pour le site (tels que le menu, l'entête, le pied de page, ou autres scripts JavaScript utilisés par toutes les pages).
Par défaut, lors de la création d'un site web utilisant le moteur de vue Razor le fichier de Layout suivant est créé automatiquement :
@inherits System.
Web.
Mvc.
WebViewPage
<!
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
>
<
html xmlns=
"http://www.w3.org/1999/xhtml"
>
<
head>
<
title>
@View.
Title</
title>
<
link href=
"@Url.Content("
~/
Content/
Site.
css")"
rel=
"stylesheet"
type=
"text/css"
/>
</
head>
<
body>
<
div class
=
"page"
>
<
div id=
"header"
>
<
div id=
"title"
>
<
h1>
My MVC Application</
h1>
</
div>
<
div id=
"logindisplay"
>
@Html.
Partial
(
"_LogOnPartial"
)
</
div>
<
div id=
"menucontainer"
>
<
ul id=
"menu"
>
<
li>
@Html.
ActionLink
(
"Home"
,
"Index"
,
"Home"
)</
li>
<
li>
@Html.
ActionLink
(
"About"
,
"About"
,
"Home"
)</
li>
</
ul>
</
div>
</
div>
<
div id=
"main"
>
@RenderBody
(
)
<
div id=
"footer"
>
</
div>
</
div>
</
div>
</
body>
</
html>
Si l'on regarde de plus près la page, on se rend compte que ce qui la rend spéciale est l'appel à la méthode @RenderBody().
Cette méthode, qui ne peut être appelée qu'une seule fois dans la page, va permettre de déterminer l'endroit où le code de la page utilisant le Layout va être rendu.
Par exemple, si on a la vue suivante (la vue Home.cshtml créée par défaut) :
@inherits System.
Web.
Mvc.
WebViewPage
@{
View.
Title =
"Home Page"
;
LayoutPage =
"~/Views/Shared/_Layout.cshtml"
;
}
<
h2>
@View.
Message</
h2>
<
p>
To learn more about ASP.
NET MVC visit <
a href=
"http://asp.net/mvc"
title=
"ASP.NET MVC Website"
>
http:
//asp.net/mvc</a>.
</
p>
Et que je modifie le fichier _Layout.cshtml ainsi :
<
div id=
"main"
style=
"background-color:red"
>
@RenderBody
(
)
<
div id=
"footer"
>
</
div>
</
div>
Le résultat, à l'exécution, sera le suivant :
V-A. ViewStart▲
Il peut vite être lassant d'avoir, en haut de chaque page, une ligne indiquant le fichier de layout à utiliser.
Pour remédier à cela, Razor propose un mécanisme supplémentaire, la vue _ViewStart, spécifiquement prévue pour gérer l'association des pages avec un layout.
ViewStart est en effet appelée avant chaque rendu d'une vue se trouvant dans son répertoire ou un de ses sous-répertoires.
Dans sa plus simple expression, une page ViewStart aura le contenu suivant :
@{
Layout =
"~/Views/Shared/_Layout.cshtml"
;}
Il est aussi possible d'utiliser cette vue pour sélectionner le layout à utiliser, par exemple, on pourrait, pour utiliser un layout spécifique pour les pages d'édition, utiliser le code suivant :
@{
if
(
ViewContext.
Controller.
ValueProvider[
"controller"
].
RawValue.
StartsWith
(
"Edit"
))
{
Layout =
"~/Views/Shared/_EditLayout.cshtml"
;
}
else
{
Layout =
"~/Views/Shared/_Layout.cshtml"
;
}
}
V-B. Sections▲
Le fait de n'avoir qu'un seul appel possible à RenderBody va certainement faire lever les sourcils de ceux qui utilisaient des masterpages ayant plusieurs zones de rendu possibles (plusieurs ContentPlaceHolders).
Razor apporte une solution élégante à cette problématique, par l'intermédiaire des sections.
Une section est une zone de la page qui va être définie ou redéfinie au niveau de la vue qui appelle le layout.
Par exemple, supposons que nous voulions, au niveau d'une vue spécifique, par exemple la vue Home, afficher un pied de page particulier, nous pourrions faire, dans notre vue _Layout :
<
div id=
"main"
>
@RenderBody
(
)
@RenderSection
(
"PiedDePage"
)
// .
Et, dans notre vue Home.cshtml :
@{
Layout =
"~/Views/Shared/_Layout.cshtml"
;
}
@section PiedDePage {
<
div>
Mon pied de page pour la page Home</
div>
}
Ceci étant dit, toute vue appelant _Layout va dorénavant devoir avoir une section Footer, au risque de remonter une exception. Nous avons le choix de rendre cette section optionnelle. Pour cela, il nous suffit de modifier l'appel à RenderSection, en mettant l'attribut required à false. Cela se passe comme cela :
@RenderSection
(
"PiedDePage"
,
required:
false
)
Cela est bien joli, mais pour vraiment couvrir les fonctionnalités de la masterpage, il nous manque toujours la possibilité d'avoir du contenu par défaut. Pour cela, on va tester l'existence de la section sur laquelle on travaille, et appliquer un rien de logique conditionnelle dans notre layout.
Notre layout aura dorénavant le contenu suivant :
@RenderBody
(
)
@if
(
IsSectionDefined
(
"PiedDePage"
)) {
@RenderSection
(
"PiedDePage"
)
}
else
{
<
div>
Mon pied de page par défaut </
div>
}
V-C. Vues partielles avec Razor▲
L'exemple que nous venons de voir, bien que fonctionnel, est relativement tiré par les cheveux. En effet, dans la « vraie vie » d'un projet, un pied de page sera généralement soit géré directement depuis le layout, soit déporté dans une vue partielle.
En effet, tout comme lorsque l'on utilise le moteur de rendu aspx, Razor permet de créer et de manipuler des vues partielles, et introduit une syntaxe spécifique pour ces vues.
On va donc transformer notre pied de page en vue partielle, et utiliser la méthode RenderPage pour l'appeler.
Pour cela, on va commencer par créer un nouveau fichier cshtml
Notez que nous avons préfixé le nom de la vue par un caractère souligné (underscore). Cette convention, héritée de WebPages, indique que PiedDePage est une classe partielle, qui ne peut pas être rendue directement.
Pour conserver la démonstration la plus simple possible, on va commencer avec une vue partielle qui n'a pas besoin de données supplémentaires, et dont le code est décrit ci-après:
<
div>
Mon pied de page par défaut </
div>
Oui, c'est tout, rien de plus compliqué que ça.
Notre fichier de layout, lui, va être modifié ainsi :
<
div id=
"main"
>
@RenderBody
(
)
@RenderPage
(
"~/Views/Shared/_PiedDePage.cshtml"
)
// ...
Notez que vous arriveriez exactement au même résultat en utilisant la méthode RenderPartial, avec le code suivant :
{@Html.RenderPartial("_PiedDePage");}
Si notre page est plus complexe (comprendre, très certainement pas la page layout), et que nous devions passer, par exemple, la valeur Url de notre Model à notre vue partielle, il nous suffirait de faire, dans le fichier appelant la vue partielle :
@RenderPage("~/Views/Shared/_MaVuePartielleComplexe.cshtml", Model.Url)
Il nous faudra par contre modifier notre Vue partielle, pour la typer avec le type correspondant. Par exemple, en supposant que notre valeur Url est un string :
@model string
<
div>
Mon pied de page par défaut,
avec l'Url @Model </div>
Il est possible d'utiliser des modèles dynamiques dans les vues partielles. Personnellement, cette méthode ne me réjouit pas plus que cela, je préfère donc ne pas la démontrer dans cet article ;)