2010-07-29 23 views
128

Sono uno sviluppatore Java nuovo su .NET. Sto lavorando a un progetto .Net MVC2 in cui voglio avere una vista parziale per avvolgere un widget. Ogni oggetto widget JS ha un oggetto dati JSON che verrà popolato dai dati del modello. Quindi metodi per aggiornare questi dati associati agli eventi quando i dati vengono modificati nel widget o se tali dati vengono modificati in un altro widget. Il codice è qualcosa di simile.ASP.NET MVC: Come convertire View Model in oggetto Json

MyController

virtual public ActionResult DisplaySomeWidget(int id) 
{ 
    SomeModelView returnData = someDataMapper.getbyid(1); 

    return View(myview, returnData); 
} 

myview.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SomeModelView>" %> 

    <script type="text/javascript"> 

     //creates base widget object; 
     var thisWidgetName= new Widget(); 

     thisWidgetName.updateTable= function() { 
      // UpdatesData 
     }; 

      $(document).ready(function() { 
       thisWidgetName.data = <% converttoJSON(model) %> 
       $(document).bind('DATA_CHANGED', thisWidgetName.updateTable()); 
      }); 
     </script> 

    <div><%:model.name%></div> 

Quello che non so è come inviare i dati su come SomeModelView e poi essere in grado di utilizzare che per popolare il widget e convertirlo in Json. Mi sembravano dei modi molto semplici per farlo nel controller ma non nella vista. Immagino che questa sia una domanda di base che sto cercando da poche ore di fare questa chiazza di petrolio.

+1

So che questa è una vecchia questione. Ma ad oggi ci sono modi migliori per farlo. Non mischiare JSON in linea con il tuo risultato di visualizzazione. JSON è facilmente serializzabile tramite AJAX e può essere trattato come oggetti. Qualunque cosa in JavaScript dovrebbe essere separata dalla vista. È possibile restituire facilmente i modelli senza alcuno sforzo tramite un controller. – ppumkin

risposta

296

In mvc3 con rasoio @Html.Raw(Json.Encode(object)) sembra fare il trucco.

+1

+1 ho usato Html.Raw, ma mai trovato Json.Encode e appena usato JavaScriptSerializer per aggiungere la stringa nel controller al modello di vista – AaronLS

+0

che funziona come un fascino. Penso che funzioni meglio nella vista in certi casi. – PJH

+4

Questo approccio funziona anche quando si desidera passare il JSON risultante a Javascript. Razor si lamenta con i ghirigori verdi se si mette il codice @ Html.Raw (...) come parametro di funzione all'interno

4

È possibile utilizzare Json dall'azione diretta,

La vostra azione sarebbe qualcosa di simile:

virtual public JsonResult DisplaySomeWidget(int id) 
{ 
    SomeModelView returnData = someDataMapper.getbyid(1); 
    return Json(returnData); 
} 

Modifica

Appena visto che si assume questo è il Model di un Guarda come sopra non è strettamente corretto, dovresti fare una chiamata al metodo controller per ottenere questo, il ascx non lo farebbe quindi ho un modello di per sé, lascerò il mio codice solo nel caso sia utile per te e puoi modificare la chiamata

+1

ma non può rendere questo nella vista, dovrebbe effettuare una seconda chiamata Ajax –

+0

@Andrew Bullock appena modificato una volta mi sono reso conto – Pharabus

+0

Grazie, stavo originariamente facendo questo usando una chiamata jQuery get json e stavo progettando di avere l'HTML gli elementi li popolano da json. Tuttavia, lo schema che stiamo seguendo in questo momento è che le nostre viste dovrebbero restituire un modelView e questo è il modo più semplice per popolare elementi HTML di base come tabelle ecc. Potrei inviare gli stessi dati in formato JSON come ViewData ma sembra uno spreco. –

28

Ben fatto, hai appena iniziato a utilizzare MVC e hai trovato il suo primo maggiore difetto.

Non si desidera veramente convertirlo in JSON nella vista e non si desidera realmente convertirlo nel controller, poiché nessuna di queste posizioni ha senso. Sfortunatamente, sei bloccato con questa situazione.

La cosa migliore che ho trovato a fare è inviare il JSON alla vista in un ViewModel, come questo:

var data = somedata; 
var viewModel = new ViewModel(); 
var serializer = new JavaScriptSerializer(); 
viewModel.JsonData = serializer.Serialize(data); 

return View("viewname", viewModel); 

quindi utilizzare

<%= Model.JsonData %> 

nella vista. Essere consapevoli del fatto che lo standard .NET JavaScriptSerializer è piuttosto schifoso.

farlo nel controller almeno rende testabili (anche se non esattamente come il precedente - probabilmente si vuole prendere un ISerializer come una dipendenza in modo da poter prendere in giro esso)

aggiornamento anche, per quanto riguarda il tuo JavaScript , sarebbe buona norma avvolgere TUTTO il widget di JS si dispone sopra in questo modo:

(
    // all js here 
)(); 

questo modo se si mette più widget in una pagina, non sarà possibile ottenere i conflitti (a meno che non è necessario per accedere ai metodi da altrove nella pagina, ma in quel caso dovresti registrare il widget wi comunque alcuni framework di widget).Potrebbe non essere un problema ora, ma sarebbe una buona pratica aggiungere le parentesi ora per risparmiare troppo sforzo in futuro quando diventa un requisito, è anche una buona pratica OO per incapsulare la funzionalità.

+1

Questo sembra interessante. Stavo per fare una copia dei dati come json e passarla come viewData ma in questo modo sembra più interessante. Giocherò con questo e ti faccio sapere. A proposito, questa è la prima volta che faccio una domanda su StackOverflow e ci sono voluti 1,5 minuti per ottenere buone risposte, è fantastico !! –

+0

Cosa c'è di sbagliato in 'JavaScriptSerializer'? –

+0

nullità _wrong_ con essa, la sua non solo personalizzabile, se volete qualsiasi valore personalizzato formattazione bisogna farlo prima mano, in pratica rendendo tutto una stringa :(questo è più evidente con date. So che ci sono semplici soluzioni alternative, ma shouldnt essere necessario! –

0

Andrew ha avuto una grande risposta, ma ho voluto farlo un po '. Il modo in cui questo è diverso è che mi piace che le mie ModelView non contengano dati di sovraccarico. Solo i dati per l'oggetto. Sembra che ViewData si adatta al conto per i dati overhead, ma ovviamente sono nuovo in questo. Suggerisco di fare qualcosa di simile.

controller

virtual public ActionResult DisplaySomeWidget(int id) 
{ 
    SomeModelView returnData = someDataMapper.getbyid(1); 
    var serializer = new JavaScriptSerializer(); 
    ViewData["JSON"] = serializer.Serialize(returnData); 
    return View(myview, returnData); 
} 

View

//create base js object; 
var myWidget= new Widget(); //Widget is a class with a public member variable called data. 
myWidget.data= <%= ViewData["JSON"] %>; 

ciò che fa per voi è che ti dà gli stessi dati nel vostro JSON come nel tuo modelview in modo da poter potenzialmente restituire il JSON torna al tuo controller e avrebbe tutte le parti. Questo è simile al solo richiederlo tramite un JSONRequest, ma richiede una chiamata in meno in modo da risparmiare l'overhead. BTW questo è funky per Date ma sembra un altro thread.

18

ho trovato che fosse molto bello da fare in questo modo (l'uso della vista):

@Html.HiddenJsonFor(m => m.TrackingTypes) 

Qui è il secondo di classe metodo di supporto di estensione:

public static class DataHelpers 
{ 
    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) 
    { 
     return HiddenJsonFor(htmlHelper, expression, (IDictionary<string, object>) null); 
    } 

    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) 
    { 
     return HiddenJsonFor(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); 
    } 

    public static MvcHtmlString HiddenJsonFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) 
    { 
     var name = ExpressionHelper.GetExpressionText(expression); 
     var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 

     var tagBuilder = new TagBuilder("input"); 
     tagBuilder.MergeAttributes(htmlAttributes); 
     tagBuilder.MergeAttribute("name", name); 
     tagBuilder.MergeAttribute("type", "hidden"); 

     var json = JsonConvert.SerializeObject(metadata.Model); 

     tagBuilder.MergeAttribute("value", json); 

     return MvcHtmlString.Create(tagBuilder.ToString()); 
    } 
} 

Non è super -sofisticato, ma risolve il problema di dove metterlo (in Controller o in vista?) La risposta è ovviamente: né;)

+0

Questo è stato bello e pulito secondo me e racchiuso in un helper riutilizzabile. Saluti, J – John

0

Estendere la grande risposta da Dave. È possibile creare un semplice HtmlHelper.

public static IHtmlString RenderAsJson(this HtmlHelper helper, object model) 
{ 
    return helper.Raw(Json.Encode(model)); 
} 

E secondo lei:

@Html.RenderAsJson(Model) 

In questo modo è possibile centralizzare la logica per la creazione del JSON se, per qualche motivo, vuole cambiare la logica di seguito.

3

@ Html.Raw (Json.Encode (oggetto)) può essere utilizzato per convertire l'oggetto vista modale a JSON