2009-09-24 10 views
19

J'utilise ASP.NET MVC avec jQuery et ai beaucoup de demandes Ajax à mes contrôleurs.Vues partielles vs JSON (ou les deux)

Utilisez les vues partielles (usercontrols) pour générer la vue initiale lorsqu'une page est chargée. Ensuite, si j'ai besoin d'ajouter/remplacer des données en fonction de ma requête Ajax, je construis du HTML à partir de la réponse de Json.

Cette approche me donne le plein contrôle, à savoir. Je peux obtenir des informations supplémentaires de mon contrôleur si quelque chose s'est mal passé, puis afficher un message d'erreur basé sur cela.

Cependant, récemment, j'ai été très ennuyé par tout le travail supplémentaire qui consiste à maintenir la structure HTML à la fois dans mes vues partielles et la partie qui génère du code HTML à partir de Json. J'aime faire une requête ajax jQuery et ensuite le contrôleur retourne PartialView ("mypartialview") et puis juste utiliser jQuery pour remplacer au HTML dans la vue.

Cependant, de cette façon, je ne peux pas joindre de données supplémentaires à partir du contrôleur - c'est soit ce que la vue partielle me donne - soit rien. Au moins c'est mon point de vue actuel.

Si une validation se passe mal à un moment donné dans l'action de mon contrôleur, je ne souhaite pas renvoyer le code HTML de la vue partielle.

Alors, comment gérez-vous ce problème?

Merci d'avoir lu.

Répondre

2

Je crois que vous pourriez renvoyer le html rendu comme une chaîne - ceci pourrait alternativement être une chaîne html contenant un message d'erreur à afficher?

+1

En effet, c'est exactement ce qu'est une réponse PartialView. Et même si vous ne l'épelez pas tout à fait, voici la solution: vos méthodes AJAX retournent une vue partielle rendue en utilisant le même contrôle utilisateur que vous avez utilisé pour rendre cette partie de la page initialement. Pour ce faire, écrivez return PartialView (model) au lieu de return Json (model). –

+0

Merci. C'est en effet une meilleure explication :) – Paddy

+0

Craig, c'est ce que je fais maintenant. Mon problème est que je veux renvoyer à la fois Html et Json - ou à mettre autrement: Je veux que le code HTML résultant soit retourné à partir de PartialView et ensuite envelopper dans Json, afin que je puisse aussi envoyer d'autres données. Un peu comme ça mec: http://stackoverflow.com/questions/1168791/returning-a-rentered-html-partial-in-a-json-property-in-asp-net-mvc Je suppose Ce qui précède fonctionnerait, mais j'aimerais savoir comment les autres gèrent cela. – bgeek

18

Basé sur this stackoverflow anwser Je viens juste de faire la même chose.

Commencez par créer une méthode d'extension pour la classe de contrôleur.

public static string RenderViewToString(this Controller controller, string viewName, object model) 
{ 
    using (var writer = new StringWriter()) 
    { 
     var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName); 
     controller.ViewData.Model = model; 
     var viewCxt = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, writer); 
     viewCxt.View.Render(viewCxt, writer); 
     return writer.ToString(); 
    } 
} 

Ensuite, renvoyez le fichier json dans la méthode d'action des contrôleurs.

return Json(new 
          { 
           Html = this.RenderViewToString("MyView", model), 
           SomeExtraData = data 
          }); 

Vos requêtes ajax recevront désormais json avec le html qu'il contient. Je continue d'expérimenter avec cette approche par rapport au retour des fragments Html.

Espérons que cela aide.

EDIT Mise à jour pour fonctionner avec un rasoir

4

est ici un moyen de tirer profit du Content-Type retourné par chaque résultat respectif. Nous utilisons ceci pour renvoyer des informations d'erreur, et il y a déjà JS en place pour afficher les messages, donc nous obtenons la vue partielle que nous voulons, ou contrôlons les informations pour le rapport d'erreurs etc.

Pour référence, une vue partielle retourne text/html et une réponse JSON doit renvoyer application/json.

Comme d'habitude, la partie amusante est du côté javascript, et le JQuery ajax() ne déçoit pas ici!Dans votre contrôleur, renvoyez simplement PartialView() ou Json(model,) selon le cas; nous l'utilisons au format try/catch.

public ActionResult Edit(int id) { 
    try { 
     var model = getYourModel(); 
     return PartialView("Edit", model); 
    } 
    catch (Exception ex) { 
     var mi = new MessageInfo(MessageType.Error, String.Format("Edit failed: {0}", ex.Message), true); 
     return Json(mi, "application/json", JsonRequestBehavior.AllowGet); 
    } 
} 

Du côté JS, nous utilisons la fonction suivante. Notez que vous devez rétablir tous les événements JQuery que vous avez pris en charge dans $(document).ready() à partir du GET de niveau page initial, nous avons donc un paramètre de rappel.

function getPartialView(action, controller, model, divId, callback) { 
    var url = "/" + controller + "/" + action + "/"; 
    $.ajax({ 
     type: "GET", 
     url: url, 
     data: model, 
     success: function (data, textStatus, jqXHR) { 
      var ct = jqXHR.getResponseHeader("Content-Type"); 
      var mx = ct.match("text\/html"); 
      if (mx != null) { 
       $(divId).html(data); 
       if (callback) { 
        callback($(divId)); 
       } 
      } 
      else { 
       addMessage(data.type, data.title, data.text, data.sticky); 
      } 
     }, 
     error: function (jqXHR, textStatus, errorThrown) { 
      addMessage(3, "\"" + url + "\": Failed", textStatus + ": " + errorThrown, false); 
     } 
    }); 
} 

Le seul peu délicat est la vérification de l'en-tête Content-Type dans la réponse, et de se comporter en conséquence. Notez que nous "trichons" en supposant JSON si ce n'était pas du HTML. Nous appelons notre fonction addMessage() pré-existante, faites ce que vous avez besoin! Enfin, voici un exemple d'élément Anchor avec onclick ciblant getPartialView() ci-dessus.

<a href="#" onclick="getPartialView('Action', 'Controller', model, '#partialviewdivid', function(dvx) { connectJqueryEvents(dvx); })">Cancel</a> 

Great Works ...

Sauf pour Transmet le formulaire via Ajax.BeginForm() où la charge utile JSON est traitée par erreur comme HTML en raison d'une validation insuffisante des Content-Type. Le résultat est que votre div reçoit du JSON, ce qui n'est pas rendu au format HTML. Le rappel AjaxOptions.OnSuccess s'exécute, mais il est trop tard pour votre DOM à ce stade!

Il existe une solution simple, mais malheureusement, il nécessite une petite réparation à jquery-unobtrusive-ajax.js parce que la fonction asyncOnSuccess() était à courte vue comme écrit.

function asyncOnSuccess(element, data, contentType) { 
    var mode; 

    if (contentType.indexOf("application/x-javascript") !== -1) { 
    return; 
    } 
    if (contentType.indexOf("application/json") !== -1) { 
    return; 
    } 
...snip... 
} 

Dans la version OOTB, la deuxième déclaration if manque; l'ajouter est le correctif nécessaire pour l'empêcher de claquer votre charge utile JSON dans le DOM.

Avec ce correctif en place, la charge utile JSON passe dans votre AjaxOptions.OnSuccess Javascript, et vous pouvez procéder comme nécessaire.

Oui vous pouvez obtenir deux

Espérons que vous le savez, puisque vous envoyez JSON, vous pouvez renvoyer tout type de modèle, et de laisser le genre Javascript dehors; hasOwnProperty() est très pratique là-bas. Donc, évidemment, vous pouvez renvoyer une vue HTML via déjà mentionné RenderViewToString().

0

Vous pouvez également procéder de cette manière, à l'intérieur de votre contrôleur.

protected string RenderPartialViewToString(string viewName, object model) 
{ 
    if (string.IsNullOrEmpty(viewName)) 
     viewName = ControllerContext.RouteData.GetRequiredString("action"); 

    ViewData.Model = model; 

    using (StringWriter sw = new StringWriter()) { 
     ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName); 
     ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw); 
     viewResult.View.Render(viewContext, sw); 

     return sw.GetStringBuilder().ToString(); 
    } 
} 
Questions connexes