Astuces pour le moteur Razor d’ASP.Net MVC 3

Dans un post précédent, je présentais la sortie d’ASP.Net MVC3 ainsi que ses nouveautés. Parmi celles-ci, le moteur de rendu Razor, ce nouveau moteur simplifiant au maximum les vues MVC pour les rendre concises et clair. Adieu les tags ASP.Net dérangeant, il est possible de s’approcher du template comme nous l’aurions en PHP en mêlant directement le code C# au sein de votre XHTML

  @foreach (var item in Model) 
    { 
        <tr>
            <td>
                @{Html.ActionImage("../../Content/Images/edit-icon.gif", "Editer", "Edit",
 new { id = item.Id });} |
            </td>
            <td>
               <a href='@item.Url' target='_blank'>@item.Title</a>
            </td>
       </tr>
}

Si l’utiliser est enfantin dans la majorité des cas et rend votre vue claire et lisible comme de l’eau de roche, il n’en reste pas que dans certains cas, des astuces d’écritures seront nécessaires pour obtenir un résultat satisfaisant. Voici quatre exemples que j’ai rencontré lors de la migration d’un projet ASP.Net MVC 2 vers MVC 3 + Razor

Exemple numéro 1 : Générer une URL d’image dynamique

en ASP.Net MVC, nous avions quelques du genre

<img src=”images/<%=model.Id %>.gif” />

avec le moteur RAZOR, vous auriez quelque chose comme ceci

<img src=”images/@model.Id.gif” />

Malheureusement, ceci ne marche pas car le moteur est incapable de savoir si “.gif”est une propriété de notre objet ou non. Le moteur vous renvoie alors l’erreur suivante CS1061: 'string' does not contain a definition for 'gif' and no extension method 'gif' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

  La solution consiste alors à encapsuler notre objet comme ceci :

 <img src=”images/@{@model.Id}.gif” /> 
 ou encore plus proprement, en utilisant simplement des parenthèses
 <img src=”images/@(@model.Id).gif” /> 

Cet exemple de parenthèse pourra servir pour insérer le résultat d’un code C# comme l’exemple suivant :

 @foreach (var item in Model) { 
      <tr>
            <td>@(count++)</td>
            <td>@(model.Age +1)</td>
      </tr>
}
 

Exemple 2 : inclusion d’éléments avec le foreach

Deuxième exemple relativement simple. Un foreach contenant un IF permettant d’insérer du HTML sous condition

 <table>
    <tr>
    <% foreach (var item in Model) { 
           if(count %3 == 0 && count !=0)    
           Response.Write("</tr><tr>");
     %>
    
    <td>
        <a href="../Books/Details/<%=item.Id %>"> 
            <br /><%= Html.Encode(item.Title) %>
        </a>
    </td>
    
    <%
    count++;
       } 
     %>
    </tr>
</table>
 ce qui en simple modification Razor donnerait ceci:
 <table>
    <tr>
    @foreach (var item in Model) { 
           if(count %3 == 0 && count !=0)    
           {
              </tr><tr>
           }     
    <td>
        <a href="../Books/Details/@item.Id"> 
            <br />@item.Title
        </a>
    </td>
    
         count++;
    } 
    </tr>
</table>

Notez que si vous mettez un @ devant le IF, cela est signalé comme une erreur car, déjà contenu dans le foreach, le @ devient surperflu. Néanmoins, si vous l’omettez (comme le code d’exemple), alors le foreach ne trouve pas son accolade fermante et ainsi aucune solution ne convient (the foreach block is missing a closing "}" character. Make sure you have a matching "}" character for all the "{" characters within this block, and that none of the "}" characters are being interpreted as markup. ). La première solution consiste alors à utiliser WriteLiteral

 if(count %3 == 0 && count !=0)    
{
     WriteLiteral(</tr><tr>)
} 

mais une meilleur solution consiste à utiliser @:

 if(count %3 == 0 && count !=0)    
{
     @:</tr><tr>
} 
Cette syntaxe sert à indiquer que tout ce qui suit (sur la même ligne) est à considérer comme 
 du markup.
 
 
  
 Exemple 3 :  affichage de texte sans tags HTML
 Il est important de savoir que le moteur de Razor analyse chaque mot du fichier de la vue 
 pour détecter s’il s’agit de code ou de markup (balise HTML) pour savoir comment 
 afficher le contenu. Malheureusement, en cas d’absence de tag HTML, celui-ci 
 sera incapable de reconnaitre le texte à afficher.
 Voici un exemple simple
 @if(model.User.Role == “Administrateur”)
{
   La personne est un administrateur
}
 La solution consiste à ajouter des tags HTML autour du texte
 @if(model.User.Role == “Administrateur”)
{
<span>
   La personne est un administrateur
</span>
}
 Mais si vous ne souhaitez vraiment pas que ces tags soient présents dans le rendu 
 final alors il est possible d’utiliser la balise <text>

 @if(model.User.Role == “Administrateur”)
{
<text>
   La personne est un administrateur
</text>
}
  
 

Exemple 4 : utilisation des helpers de vos anciens projets ASP.Net MVC 2

Lorsque vous aviez un markup HTML dynamique à afficher sur plusieurs vues, il était conseillé de créer une classe C# statique implémentant une méthode d’extension pour l’objet HtmlHelper.

 public static class MenuHelper
{
        public static string Menu(this HtmlHelper helper, string titre)
        {
            return String.Format("<b>{0}</b>",titre);
        }
}

pour l’appeler ainsi dans votre code ASP.Net

<%=Html.Menu(monTitre) %>

Malheureusement, si vous l’utilisez tel quel au sein de votre code Razor:

@Html.Menu(monTitre)

Vous obtiendrez l’erreur suivante CS1502: The best overloaded method match for 'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)' has some invalid arguments ou bien selon la façon dont vous l’appelerez, le texte ne sera pas affiché où vous le souhaitez sur le HTML final.

La solution consiste alors à retourner non plus un objet string mais MvcHtmlString ou bien, dans le cas de grosse donnée à retourner, à écrire directement dans le buffer de sortie comme le montre l’exemple suivant

 public static class MenuHelper
{
        public static string Menu(this HtmlHelper helper, string titre)
        {
           helper.ViewContext.Writer.Write(String.Format("<b>{0}</b>",titre));
        }
}

Exemple 5 : inclure du code Javascript dynamique dans vos pages Razor

Qui n’a jamais eu besoin de créer un code javascript dont une partie serait définie par du code C#? Code l’exemple numéro 3 plus haut, le moteur Razor est incapable de faire la différence entre le code C# et le code javascript. La même solution est alors nécessaire : l’utilisation de la balise <text>

 <script type="text/javascript">
@{
    <text>
    var temp = confirm("Tu es sûr?");
    </text>
}
</script>

C’est tout pour le moment. Je viendrai compléter ce post au fil des problématiques rencontrées