159 votes

Comment convertir le View Model en objet JSON en ASP.NET MVC ?

Je suis un développeur Java, novice en .NET. Je travaille sur un projet .NET MVC2 dans lequel je veux avoir une vue partielle pour envelopper un widget. Chaque objet widget JavaScript possède un objet de données JSON qui serait alimenté par les données du modèle. Ensuite, les méthodes de mise à jour de ces données sont liées à des événements lorsque les données sont modifiées dans le widget ou si ces données sont modifiées dans un autre widget.

Le code est quelque chose comme ceci :

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>

Ce que je ne sais pas, c'est comment envoyer les données en tant que SomeModelView et ensuite être capable de l'utiliser pour remplir le widget ainsi que de le convertir en JSON. J'ai vu des moyens très simples de le faire dans le contrôleur, mais pas dans la vue. J'imagine que c'est une question basique, mais j'ai passé quelques heures à essayer de rendre cela simple.

1 votes

Je sais que c'est une vieille question. Mais à partir d'aujourd'hui, il y a de meilleures façons de le faire. Ne mélangez pas JSON en ligne avec votre résultat de vue. JSON est facilement sérialisé via AJAX et peut être traité comme des objets. Tout ce qui est en JavaScript doit être séparé de la vue. Vous pouvez facilement retourner des modèles sans aucun effort via un contrôleur.

0 votes

@PiotrKula Parfois, l'ordre d'initialisation suggère une préférence concernant l'endroit où JavaScript est inclus et assigné. Il y a toujours un niveau d'effort, mais il varie parfois en fonction de l'endroit où il est placé. Les déclarations et initialisations JavaScript en ligne sont acceptables dans une vue pour éviter les désagréments et les efforts supplémentaires.

358voto

Dave Points 6065

Dans mvc3 avec razor @Html.Raw(Json.Encode(object)) semble faire l'affaire.

1 votes

+1 J'ai utilisé Html.Raw, mais je n'ai jamais trouvé Json.Encode et j'ai juste utilisé JavaScriptSerializer pour ajouter la chaîne dans le contrôleur au modèle de vue

0 votes

Ça marche comme sur des roulettes. Je pense que ça marche mieux dans la vue dans certains cas.

6 votes

Cette approche fonctionne même lorsque vous voulez passer le JSON résultant à Javascript. Razor se plaint avec des gribouillis verts si vous placez le code @Html.Raw(...) en tant que paramètre de fonction à l'intérieur des balises <script>, mais le JSON arrive effectivement jusqu'au JS appelé. Très pratique et astucieux. +1

31voto

Andrew Bullock Points 14899

Bravo, vous venez à peine de commencer à utiliser MVC et vous avez trouvé son premier défaut majeur.

Vous n'avez pas vraiment envie de le convertir en JSON dans la vue, et vous n'avez pas vraiment envie de le convertir dans le contrôleur, car aucun de ces emplacements n'a de sens. Malheureusement, vous êtes coincé avec cette situation.

La meilleure chose que j'ai trouvé à faire est d'envoyer le JSON à la vue dans un ViewModel, comme ceci :

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

return View("viewname", viewModel);

puis utiliser

<%= Model.JsonData %>

à votre avis. Sachez que le JavaScriptSerializer standard de .NET est plutôt merdique.

le faire dans le contrôleur le rend au moins testable (même si ce n'est pas exactement comme ci-dessus - vous voudrez probablement prendre un ISerializer comme dépendance pour pouvoir le simuler).

Mise à jour De plus, en ce qui concerne votre JavaScript, il serait bon d'envelopper TOUS les widgets JS que vous avez ci-dessus de la manière suivante :

(
    // all js here
)();

De cette façon, si vous placez plusieurs widgets sur une page, vous n'aurez pas de conflits (à moins que vous n'ayez besoin d'accéder aux méthodes à partir d'un autre endroit de la page, mais dans ce cas, vous devriez de toute façon enregistrer le widget auprès d'un framework de widgets). Ce n'est peut-être pas un problème aujourd'hui, mais il serait bon d'ajouter les parenthèses maintenant pour vous épargner beaucoup d'efforts à l'avenir, lorsque cela deviendra une exigence, c'est aussi une bonne pratique OO d'encapsuler la fonctionnalité.

1 votes

Cela semble intéressant. J'allais simplement faire une copie des données en json et la passer en tant que viewData mais de cette façon, ça semble plus intéressant. Je vais jouer avec ça et je vous le ferai savoir. BTW c'est la première fois que je poste une question sur stackoverflow et il m'a fallu quoi 5 minutes pour obtenir de bonnes réponses, c'est génial ! !!

0 votes

Rien mauvais avec lui, ce n'est tout simplement pas personnalisable, si vous voulez un formatage de valeur personnalisé, vous devez le faire à l'avance, en faisant tout une chaîne de caractères :( c'est le plus important avec les dates. je sais qu'il y a des solutions de contournement faciles, mais elles ne devraient pas être nécessaires !

0 votes

@Mise à jour J'allais envisager d'utiliser un compteur statique .net de ce widget pour générer un nom d'objet unique. Mais ce que vous avez écrit semble accomplir la même chose et le faire plus facilement. J'ai également envisagé de lier la création d'un widget 'new_widget_event' à un objet au niveau de la page pour en assurer le suivi, mais la raison initiale pour laquelle je l'ai fait est devenue OBE. Merci pour toute l'aide que vous m'avez apportée. Je vais poster une version modifiée de ce que vous avez suggéré, mais je vais expliquer pourquoi je l'aime mieux.

18voto

Wolfgang Points 91

J'ai trouvé que c'était plutôt agréable de faire comme ça (utilisation dans la vue) :

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

Voici la méthode d'aide correspondante de la classe Extension :

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

Ce n'est pas super sophistiqué, mais cela résout le problème de savoir où le mettre (dans le contrôleur ou dans la vue ?) La réponse est évidemment : aucun des deux ;)

0 votes

C'était agréable et propre à mon avis et emballé dans une aide réutilisable. Santé, J

6voto

Pharabus Points 4664

Vous pouvez utiliser Json de l'action directement,

Votre action ressemblerait à ceci :

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

Editar

Je viens de voir que vous supposez que c'est le Model d'une vue, donc ce qui précède n'est pas tout à fait correct, il faudrait faire une modification de l'ordre de grandeur de la vue. Ajax à la méthode du contrôleur pour l'obtenir, l'élément ascx n'aurait pas de modèle à proprement parler, je vais laisser mon code au cas où il vous serait utile et où vous pourriez modifier l'appel.

Edit 2 j

0 votes

Merci, à l'origine je faisais cela en utilisant un appel jQuery get json et j'avais prévu que les éléments HTML se remplissent eux-mêmes à partir de json. Cependant, le modèle que nous suivons actuellement est que nos vues doivent retourner un modelView et c'est le moyen le plus facile de remplir les éléments HTML de base comme les tableaux, etc. Je pourrais envoyer les mêmes données au format JSON en tant que ViewData mais cela semble être un gaspillage.

2 votes

Vous ne devriez PAS incorporer du JSON dans un résultat d'affichage. Le PO doit soit s'en tenir à la pratique MVC standard et renvoyer un modèle ou un modèle de vue, soit faire un AJAX. Il n'y a absolument AUCUNE RAISON d'intégrer JSON à une vue. C'est tout simplement du code très sale. Cette réponse est la voie correcte et la voie recommandée par les pratiques de Microsoft. Le downvote était inutile, c'est définitivement la bonne réponse. Nous ne devrions pas encourager les mauvaises pratiques de codage. Soit JSON via AJAX, soit les modèles via les vues. Personne n'aime le code spaghetti avec un balisage mixte !

0 votes

Cette solution entraînera le problème suivant : stackoverflow.com/questions/10543953/

0voto

Chris Stephens Points 499

Andrew a donné une excellente réponse, mais je voulais la modifier un peu. La différence est que j'aime que mes ModelViews ne contiennent pas de données générales. Juste les données de l'objet. Il semble que ViewData convienne pour les données générales, mais bien sûr, je suis nouveau dans ce domaine. Je suggère de faire quelque chose comme ceci.

Contrôleur

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

Voir

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

Cela vous donne les mêmes données dans votre JSON que dans votre ModelView. Vous pouvez donc potentiellement renvoyer le JSON à votre contrôleur et il aura toutes les parties. C'est similaire à une simple demande via un JSONRequest, mais cela nécessite un appel de moins, ce qui vous évite cette surcharge. BTW, c'est funky pour les dates mais cela semble être un autre sujet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X