2009-09-24 13 views
19

Uso ASP.NET MVC con jQuery e ho molte richieste Ajax sui miei controller.Viste parziali vs Json (o entrambi)

Utilizzare le viste parziali (usercontrols) per creare la vista iniziale quando viene caricata una pagina. Quindi se ho bisogno di aggiungere/sostituire i dati in base alla mia richiesta Ajax, costruisco HTML dalla risposta Json.

Questo approccio mi dà il controllo completo, vale a dire. Posso ottenere ulteriori informazioni dal mio controller se qualcosa è andato storto e quindi mostrare un messaggio di errore basato su quello.

Tuttavia, recentemente sono stato davvero infastidito da tutto il lavoro extra che riguarda il mantenimento della struttura HTML sia nelle mie viste parziali che nella parte che genera HTML da Json.

Mi piace fare una richiesta jQuery ajax e quindi il controller restituire PartialView ("mypartialview") e quindi utilizzare solo jQuery per sostituire in HTML nella vista.

Tuttavia, in questo modo non è possibile allegare dati aggiuntivi dal controller, o qualunque sia la vista parziale che fornisce o nulla. Almeno questo è il mio attuale punto di vista.

Se alcune convalide non sono corrette a un certo punto nell'azione del mio controller, non voglio restituire l'HTML della vista parziale.

Quindi, come si fa a gestire questo problema?

Grazie per la lettura.

risposta

2

Credo che si possa restituire l'html renderizzato come una stringa - questa potrebbe essere alternativamente una stringa html contenente un messaggio di errore da visualizzare?

+1

In effetti, questo è proprio ciò che una risposta di PartialView è. E anche se non lo scrivi abbastanza, questa è la soluzione: i tuoi metodi AJAX restituiscono un partial parziale renderizzato usando lo stesso controllo utente che hai usato per rendere inizialmente quella parte della pagina. Lo fai scrivendo return PartialView (modello) invece di restituire Json (modello). –

+0

Grazie. Questa è davvero una spiegazione migliore :) – Paddy

+0

Craig, questo è quello che faccio ora. Il mio problema è che voglio restituire sia Html che Json - o mettere altrimenti: voglio l'html risultante che viene restituito da PartialView restituito e quindi includerlo in Json, in modo che possa inviare anche altri dati. Un po 'come questo ragazzo: http://stackoverflow.com/questions/1168791/returning-a-rentered-html-partial-in-a-json-property-in-asp-net-mvc immagino quanto sopra funzionerebbe, ma mi piacerebbe sapere come gli altri gestiscono questo. – bgeek

18

Basato su this stackoverflow anwser Ho appena deciso di fare la stessa cosa.

Prima creare un metodo di estensione per la classe controller.

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

Quindi restituire il json nel metodo di azione dei controller.

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

vostre richieste Ajax ora riceveranno JSON con il codice HTML in esso contenute. Ancora sperimentando questo approccio sulla restituzione di semplici frammenti Html.

Spero che questo aiuti.

EDIT Aggiornato per funzionare con il rasoio

4

Ecco un modo per sfruttare il Content-Type restituito da ogni rispettivo risultato. Stiamo usando questo per rispedire informazioni di errore, e c'è già js in atto per visualizzare i messaggi, in modo da ottenere sia la vista parziale che vogliamo, o informazioni di controllo per la segnalazione degli errori, ecc

Per riferimento, una vista parziale torna text/html e una risposta JSON dovrebbe restituire application/json.

Come al solito, la parte divertente è sul lato JavaScript, e il JQuery ajax() non delude qui!

Nel controller, è sufficiente restituire PartialView() o Json(model,) come appropriato; lo stiamo utilizzando nel formato 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); 
    } 
} 

Sul lato JS, stiamo utilizzando la seguente funzione. Nota che devi ristabilire tutti gli eventi JQuery che hai agganciato in $(document).ready() dal GET di livello pagina iniziale, quindi abbiamo un parametro di callback.

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

L'unica parte difficile sta controllando la Content-Type intestazione nella risposta, e comportarsi di conseguenza. Si prega di notare che stiamo "ingannando" assumendo JSON se non fosse HTML. Stiamo chiamando la nostra funzione pre-esistente addMessage(), facciamo tutto il necessario!

Infine, ecco un esempio di elemento di ancoraggio con onclick targeting getPartialView() precedente.

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

Opere Grande ...

Fatta eccezione per la forma sostiene tramite Ajax.BeginForm() in cui il carico utile JSON è trattato erroneamente come HTML dovuta alla convalida insufficiente di Content-Type. Il risultato è che al tuo div viene aggiunto un JSON, che in pratica non viene visualizzato come HTML. Il callback AjaxOptions.OnSuccess viene eseguito, ma a questo punto è troppo tardi per il DOM!

c'è una soluzione semplice, ma purtroppo richiede un piccolo riparazione al jquery-unobtrusive-ajax.js perché la funzione asyncOnSuccess() era miope come scritto.

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

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

Nella versione OOTB, il secondo if affermazione manca; aggiungendo è la correzione necessaria per evitare di sbattere il payload JSON nel DOM.

Con questa correzione in posizione, il payload JSON passa nel tuo AjaxOptions.OnSuccess Javascript e puoi procedere se necessario.

Sì è possibile ottenere entrambi

Speriamo che si conosce in quanto si sta inviando JSON, si potrebbe restituire qualsiasi tipo di modello, e lasciare che il Javascript sorta fuori; hasOwnProperty() è utile lì. Quindi, ovviamente, è possibile inviare un po 'di vista HTML tramite il già citato RenderViewToString().

0

Si può fare anche in questo modo, Metti questo dentro il controller.

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