Restituisce Vista parziale e JSON dall’azione MVC ASP.NET

Sto introducendo KnockoutJS in un’app esistente. Il mio piano è di modificare / utilizzare le viste parziali esistenti che abbiamo già creato e associarle ai modelli di visualizzazione JS con gli attributi dichiarativi di Knockout. Quando effettuo una chiamata AJAX a un’azione, idealmente mi piacerebbe che l’azione restituisse sia l’HTML della vista parziale che l’object JSON. Quindi posso riempire un div con l’HTML, convertire il JSON in un object Knockout e collegarlo all’HTML. Ma non riesco a capire come restituire entrambi dall’azione.

Ho bisogno del modello a vista completa perché lo aggiornerò e infine lo invierò al server.

Ho pensato che l’azione restituisse la vista parziale (già legata al modello) e, all’interno della vista parziale, includi javascript per convertire il modello .Net in un object Knockout. Ma sento che spargere il JS in giro è disordinato e intrattabile. Preferirei avere tutto vicino alla chiamata originale ajax.

Suppongo che un’altra alternativa sia effettuare due chiamate di chiamata. Uno per il JSON e un altro per la vista parziale. Ma ci deve essere un modo semplice.

Qualche idea su come meglio fare questo?

Sono sicuro che ci sono molti modi per farlo. Rendo manualmente la vista dal controller e quindi restituisco la vista renderizzata come parte della mia risposta JSON.

Ciò preserva le responsabilità di ogni entity framework. Le viste si trovano ancora utilizzando il motore di visualizzazione e possono essere riutilizzate. Il controller conosce poco o nulla sulla vista oltre il suo nome e tipo di modello.

Rendering manuale

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(); } } } 

Nel tuo metodo di azione:

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

Si noti che sto restituendo un tipo anonimo. È ansible restituire qualsiasi tipo (serializzabile) desiderato, purché abbia una proprietà stringa per la vista sottoposta a rendering.

analisi

Il test di un’azione che utilizza il rendering manuale richiede una leggera modifica. Ciò è dovuto al rendering della vista un po ‘prima rispetto al rendering nella pipeline MVC.

Rendering manuale

  1. Inserisci il metodo di azione
  2. Rendering esplicito <- Ciò renderà difficile testare l'azione chiamante
  3. Esci dal metodo di azione

Rendering automatico

  1. Inserisci il metodo di azione
  2. Crea un risultato di visualizzazione
  3. Esci dal metodo di azione
  4. Elabora il risultato della vista (rendendo così la vista)

In altre parole, il nostro processo di rendering manuale avvia una serie di altre operazioni che rendono difficile il test (come l’interazione con il gestore di build per compilare la vista).

Supponendo che si desideri testare il metodo di azione e non i contenuti effettivi della vista, è ansible verificare se il codice è in esecuzione o meno in un ambiente ospitato.

  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 } 

Checking HostingEnvironment.IsHosted è economico (sotto il cofano, è semplicemente un assegno nulla).

È ansible creare un nascosto sul parziale con un valore impostato sulla stringa JSON di ViewModel. Quindi, prima di eseguire il rendering della vista parziale, prendi il valore JSON da quel campo e analizzalo. Quindi rimuovilo dalla vista parziale, inseriscilo nella tua pagina e fai ko.applyBindingsToDescendants(viewModel, $("#parentElement")[0])

Non sono del tutto sicuro di ciò che provo per questo approccio, ed è solo una teoria. Non l’ho provato ma ho il sospetto che funzionerebbe. Una trappola del booty che dovresti cercare è il browser che tenta di memorizzare nella cache la tua richiesta GET. Nella tua richiesta Ajax ti piacerebbe fare:

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

O fai solo una richiesta $.post . ( riferimento )

Quindi questa è un’opzione.