web-dev-qa-db-fra.com

Renvoyer une vue partielle et une action JSON à partir d'une action ASP.NET MVC

J'introduis KnockoutJS dans une application existante. Mon plan est de modifier/utiliser les vues partielles existantes déjà créées et de les lier aux modèles de vues JS avec les attributs déclaratifs de Knockout. Lorsque je passe un appel AJAX à une action, j'aimerais dans l'idéal qu'elle renvoie le code HTML de la vue partielle et de l'objet JSON. Ensuite, je peux remplir un div avec le code HTML, convertir le code JSON en objet Knockout et le lier au code HTML. Mais je n'arrive pas à comprendre comment revenir à la fois de l'action.

J'ai besoin du modèle de vue complète car je vais le mettre à jour et le renvoyer éventuellement au serveur.

Je pensais que l'action devait renvoyer la vue partielle (déjà liée au modèle) et inclure dans cette vue partielle le javascript permettant de convertir le modèle .Net en objet Knockout. Mais je pense que disperser le JS comme ça est désordonné et impossible à maintenir. Je préférerais que tout soit proche de l'appel initial ajax.

Je suppose qu'une autre alternative est de faire deux appels d'action. Un pour le JSON et un autre pour la vue partielle. Mais il doit y avoir un moyen plus lisse.

Des idées sur la meilleure façon de faire cela?

14
nthpixel

Je suis sûr qu'il y a différentes façons de le faire. Je rends manuellement la vue à partir du contrôleur, puis je la renvoie dans le cadre de ma réponse JSON. 

Cela préserve les responsabilités de chaque entité. Les vues sont toujours localisées à l'aide du moteur de vue et peuvent être réutilisées. Le contrôleur ne sait rien ou presque de la vue au-delà de son nom et de son type.

Rendu manuel

public static class RenderHelper
{
    public static string PartialView( Controller controller, string viewName, object model )
    {
        controller.ViewData.Model = model;

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

            viewResult.View.Render( viewContext, sw );
            viewResult.ViewEngine.ReleaseView( controller.ControllerContext, viewResult.View );

            return sw.ToString();
        }
    }
}

Dans votre méthode d'action:

object model = null; // whatever you want
var obj = new { 
    someOtherProperty = "hello", 
    view = RenderHelper.PartialView( this, "_PartialName", model ) 
};

return Json( obj );

Notez que je retourne un type anonyme. Vous pouvez renvoyer n'importe quel type (sérialisable) de votre choix, à condition qu'il possède une propriété de chaîne pour la vue rendue.

Essai

Tester une action utilisant le rendu manuel nécessite une légère modification. Cela est dû au fait que l'affichage a été rendu un peu plus tôt que dans le pipeline MVC.

Rendu manuel

  1. Entrez la méthode d'action
  2. Rendre la vue explicitement <- cela rendra difficile le test de l'action appelante
  3. Méthode d'action de sortie

Rendu automatique

  1. Entrez la méthode d'action
  2. Créer un résultat de vue
  3. Méthode d'action de sortie
  4. Traiter le résultat de la vue (rendant ainsi la vue)

En d'autres termes, notre processus de rendu manuel déclenche diverses opérations rendant le test difficile (par exemple, interagir avec le gestionnaire de génération pour compiler la vue).

En supposant que vous souhaitiez tester la méthode d'action et non le contenu réel de la vue, vous pouvez vérifier si le code s'exécute ou non dans un environnement hébergé.

    public static string PartialView( Controller controller, string viewName, object model )
    {
        // returns false from a VS 2013 unit test, true from IIS
        if( !HostingEnvironment.IsHosted )
        {
            // return whatever you want here
            return string.Empty;
        }

        // continue as usual
     }

Vérifier HostingEnvironment.IsHosted est peu coûteux (sous le capot, il s’agit simplement d’un contrôle nul).

21
Tim Medora

Vous pouvez créer un <input> masqué sur le partiel avec une valeur définie sur la chaîne JSON du ViewModel. Ensuite, avant de rendre la vue partielle, extrayez la valeur JSON de ce champ et analysez-la. Ensuite, supprimez-le de la vue partielle, insérez-le dans votre page et faites ko.applyBindingsToDescendants(viewModel, $("#parentElement")[0])

Je ne suis pas tout à fait sûr de ce que je pense de cette approche, et ce n'est qu'une théorie. Je n'ai pas testé cela, mais je pense que cela fonctionnerait. Un booty trap vous devez rechercher le navigateur qui tente de mettre en cache votre demande GET. Dans votre demande ajax, vous voudriez faire:

$.ajax({
  url: "/",
  type: 'GET',
  cache: 'false'
});

Ou faites simplement une requête $.post. ( reference )

Donc, c'est une option.

1
nwayve