2013-09-06 17 views
13

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'oggetto JSON. Quindi posso riempire un div con l'HTML, convertire il JSON in un oggetto Knockout e collegarlo all'HTML. Ma non riesco a capire come restituire entrambi dall'azione.Restituisce Vista parziale e JSON dall'azione MVC ASP.NET

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

Ho pensato di fare in modo che l'azione restituisca la vista parziale (già associata al modello) e, all'interno della vista parziale, includi javascript per convertire il modello .Net in un oggetto 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 sul modo migliore per farlo?

risposta

21

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 entità. 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.

Manuale di rendering

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 vostro 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 tornando un tipo anonimo. È possibile restituire qualsiasi tipo (serializzabile) desiderato, purché abbia una proprietà stringa per la vista sottoposta a rendering.

Testing

Test 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.

Manuale di rendering

  1. Enter metodo di azione
  2. Render vista esplicitamente < - questo renderà difficile per testare l'azione di chiamata
  3. Exit metodo di azione

automatica Rendering

  1. Enter metodo di azione
  2. creare un risultato vista
  3. Exit metodo di azione
  4. risultato pagina processo (rendendo così la vista)

In altre parole, il nostro processo di rendering manuale prende il via un varietà di altre operazioni che rendono difficile il test (come l'interazione con il gestore di build per compilare la vista).

Supponendo di voler testare il metodo di azione e non il contenuto effettivo della vista, è possibile 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 
    } 

controllando HostingEnvironment.IsHosted è poco costoso (sotto il cofano, è semplicemente un controllo nullo).

+0

+1 Non ho mai pensato a una classe di aiuto. Questa potrebbe essere la soluzione più elegante. Lo proverò. Grazie. – nthpixel

+0

Non vorresti fare: 'var obj = new {view = RenderHelper.PartialView (this," _PartialName ", viewModel), model = koViewModel};'? O è questo che 'foo' è? O mi sta sfuggendo qualcosa? – nwayve

+0

@Dennis - sì, "foo" rappresenta qualsiasi altro dato che è necessario restituire. Non sono sicuro di quale Knockout abbia bisogno sul client, ma la vista ha bisogno anche di un modello (anche se è nullo). –

1

È possibile creare un <input> 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 rimuoverlo dalla vista parziale, inserirlo nella tua pagina e fare ko.applyBindingsToDescendants(viewModel, $("#parentElement")[0])

Non sono del tutto sicuro di come mi sento in merito a questo approccio, ed è solo una teoria. Non l'ho provato ma ho il sospetto che funzionerebbe. Uno booty trap da cercare è il browser che tenta di memorizzare nella cache la richiesta GET. Nella richiesta Ajax che ci si vuole fare:

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

o semplicemente una richiesta $.post. (reference)

Quindi questa è un'opzione.

+0

+1 per l'opzione ... ma non sono sicuro di come mi sento a riguardo. – nthpixel

Problemi correlati