Update : Si vous êtes tombé ici en cherchant comment faire de l’Ajax avec ASP.NET MVC, je vous conseille d’aller voir cet article sur Tech Head Brothers : L’AjaxHelper de ASP.NET MVC

Ce post est une réaction à chaud après avoir perdu quelques heures à comprendre pourquoi mon appel Ajax ne fonctionnait pas.

Je viens d’installer la preview d’ASP.NET MVC 2, et après avoir découvert avec enthousiasme les nouveautés, j’ai décidé de tester les Ajax Helpers que je n’avais pas encore pris le temps de regarder dans la première version.

J’ai donc décidé de faire un moteur de recherche asynchrone : une textbox, un bouton pour valider, et une zone avec les résultats qui se mettra à jour toute seule sans recharger la page :

<div class="search">
<p>Recherche d'article</p> 
<p><% using (Ajax.BeginForm("AjaxSearch", "Search", 
       new AjaxOptions() { UpdateTargetId = "searchResult" })) 
      { %> 
       <%= Html.TextBox("s") %> 
       <input type="submit" value="Search"/> 
       <% } %></p> 
        
   <p id="searchResult"></p> 
</div> 

Le fonctionnement est très simple : on utilise un formulaire qui fera un appel Ajax, qu’on initialise à l’aide du Ajax.BeginForm On lui passe en paramètre le nom de l’action et du contrôleur MVC qu’on veut appeler, ainsi que les AjaxOptions : ici je demande à ce que le résultat mette à jour l’élément avec l’id “searchResult”, situé un peu plus bas.

Coté serveur, j’ai donc une action qui va me renvoyer une liste d’articles correspondant à la recherche :

public ActionResult AjaxSearch() 
{ 
    String s =  Request["s"]; 

    var data = Context.Articles.Where(a => a.Title.Contains(s)).ToList(); 

    if (data.Count == 0) 
        return Content("No article found"); 

    return View(data); 
} 

Et cette action renvoie une vue, qui est ici une vue partielle (ascx) :

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<MyMvcWiki.Models.Article>>" %> 
<ul> 
    <% foreach (var item in Model) 
       { %>     
   <li> 
   <%= Html.ActionLink(item.Title, "Details", "Wiki", new { id=item.Id }, null) %> 
   </li> 
    <% } %> 
</ul> 

Et voilà, Ajax avec MVC, c’est pas plus compliqué que ça ! Enfin, en théorie, puisqu’il suffit de tester la page pour avoir une belle erreur, avec un message très explicite afin de faciliter le débugage :

“Error: Unknown runtime error” 

Erreur d’autant plus incompréhensible que lorsque la recherche ne renvoie aucun résultat, le message “No article found” s’affiche correctement.

Je me suis donc arraché les cheveux sur cette erreur, puisque le code me semblait parfaitement correct de bout en bout, jusqu’à ce que je comprenne ce qui n’allait pas. Revoyons de plus près ce qui se passe coté client :

On fait un appel Ajax qui va mettre à jour ma page. La réponse du serveur doit mettre à jour l’élément avec l’id "searchResult"\. Dans mon code, il s’agit d’un élément <p>. La réponse du serveur renvoie une liste d’articles sous forme de liste à puces <ul> <li>. Et si on se décide à lire les spécifications du xhtml, on se rend compte que c’est interdit. Et oui, un paragraphe ne doit pas contenir d’éléments de type bloc, donc pas de <ul>

Il suffit de remplacer mon <p id="searchResult"> par un <div id="searchResult"> pour que ça fonctionne !

Je suis partagé sur cette erreur : d’un coté, c’est vrai que c’est bien de forcer les développeurs à faire du code correct, mais j’aurais quand même apprécié avoir un message d’erreur plus explicite.

En dehors de ça, j’avoue que je suis conquis pas les méthodes Ajax fournies avec MVC. Les accrocs à l’UpdatePanel ne seront pas dépaysés (enfin, pas plus qu’ils ne l’étaient en passant de WebForms à MVC). C’est très facile et rapide à mettre en place, sans avoir à écrire de javascript. Tout en laissant la possibilité aux fans de JavaScript de faire leurs appels Ajax à la main (avec jQuery par exemple, qui est inclut de base dans les nouveaux projets MVC).