Comment utiliser knockout.js avec les ViewModels ASP.NET MVC?

129

Prime

Cela fait longtemps et j'ai encore quelques questions en suspens. J'espère qu'en ajoutant une prime, ces questions obtiendront peut-être une réponse.

  1. Comment utiliser les helpers HTML avec knockout.js
  2. Pourquoi le document était-il prêt pour le faire fonctionner (voir la première modification pour plus d'informations)

  3. Comment faire quelque chose comme ça si j'utilise le mappage knockout avec mes modèles de vue? Comme je n'ai pas de fonction à cause de la cartographie.

    function AppViewModel() {
    
        // ... leave firstName, lastName, and fullName unchanged here ...
    
        this.capitalizeLastName = function() {
    
        var currentVal = this.lastName();        // Read the current value
    
        this.lastName(currentVal.toUpperCase()); // Write back a modified value
    
    };
  4. Je veux utiliser des plugins par exemple Je veux pouvoir restaurer des observables comme si un utilisateur annulait une demande Je veux pouvoir revenir à la dernière valeur. D'après mes recherches, cela semble être réalisé par des personnes qui créent des plugins comme des éditables

    Comment utiliser quelque chose comme ça si j'utilise la cartographie? Je ne veux vraiment pas aller à une méthode où j'ai dans ma vue le mappage manuel où je mappe chaque champ MVC viewMode à un champ de modèle KO car je veux aussi peu de javascript en ligne que possible et cela semble juste doubler le travail et c'est pourquoi j'aime cette cartographie.

  5. Je crains que pour faciliter ce travail (en utilisant la cartographie), je perdrai beaucoup de puissance KO, mais d'un autre côté, je crains que la cartographie manuelle ne soit beaucoup de travail et rendra mes vues contenant trop d'informations et pourrait devenir à l'avenir plus difficile à maintenir (par exemple, si je supprime une propriété dans le modèle MVC, je dois la déplacer également dans le modèle de vue KO)


Message original

J'utilise asp.net mvc 3 et je regarde KO car il a l'air plutôt cool, mais j'ai du mal à comprendre comment cela fonctionne avec asp.net mvc, en particulier les modèles de vue.

Pour moi en ce moment je fais quelque chose comme ça

 public class CourseVM
    {
        public int CourseId { get; set; }
        [Required(ErrorMessage = "Course name is required")]
        [StringLength(40, ErrorMessage = "Course name cannot be this long.")]
        public string CourseName{ get; set; }


        public List<StudentVm> StudentViewModels { get; set; }

}

J'aurais un Vm qui a des propriétés de base comme CourseName et il aura une validation simple en plus. Le modèle Vm peut également contenir d'autres modèles de vue si nécessaire.

Je passerais ensuite ce Vm à la vue où j'utiliserais des aides html pour m'aider à l'afficher à l'utilisateur.

@Html.TextBoxFor(x => x.CourseName)

Je pourrais avoir des boucles foreach ou quelque chose pour extraire les données de la collection de modèles de vue étudiant.

Ensuite, lorsque je soumettrais le formulaire, j'utiliserais jquery et je l' serialize arrayenvoyais à une méthode d'action de contrôleur qui le lierait au viewmodel.

Avec knockout.js, tout est différent car vous avez maintenant des viewmodels pour cela et de tous les exemples que j'ai vus, ils n'utilisent pas d'aide html.

Comment utilisez-vous ces 2 fonctionnalités de MVC avec knockout.js?

J'ai trouvé cette vidéo et elle explique brièvement (les dernières minutes de la vidéo à 18:48) un moyen d'utiliser les viewmodels en ayant essentiellement un script en ligne qui a le viewmodel knockout.js qui se voit attribuer les valeurs dans le ViewModel.

Est-ce la seule façon de le faire? Que diriez-vous dans mon exemple d'avoir une collection de viewmodels? Dois-je avoir une boucle foreach ou quelque chose pour extraire toutes les valeurs et les assigner à KO?

Quant aux helpers html, la vidéo n'en dit rien.

Ce sont les 2 domaines qui me déroutent complètement, car peu de gens semblent en parler et cela me laisse perplexe quant à la façon dont les valeurs initiales et tout arrivent à la vue lorsque jamais l'exemple n'est qu'un exemple de valeur codée en dur.


Éditer

J'essaie ce que Darin Dimitrov a suggéré et cela semble fonctionner (j'ai dû apporter quelques modifications à son code cependant). Je ne sais pas pourquoi j'ai dû utiliser le document prêt, mais tout n'était pas prêt sans lui.

@model MvcApplication1.Models.Test

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>Index</title>
    <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript"></script>
    <script src="../../Scripts/knockout-2.1.0.js" type="text/javascript"></script>
    <script src="../../Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
   <script type="text/javascript">

   $(function()
   {
      var model = @Html.Raw(Json.Encode(Model));


// Activates knockout.js
ko.applyBindings(model);
   });

</script>

</head>
<body>
    <div>
        <p>First name: <strong data-bind="text: FirstName"></strong></p>
        <p>Last name: <strong data-bind="text: LastName"></strong></p>
        @Model.FirstName , @Model.LastName
    </div>
</body>
</html>

J'ai dû l'enrouler autour d'un document jquery prêt à le faire fonctionner.

Je reçois également cet avertissement. Je ne sais pas de quoi il s'agit.

Warning 1   Conditional compilation is turned off   -> @Html.Raw

J'ai donc un point de départ que je suppose que je mettrai au moins à jour lorsque j'aurai fini de jouer et comment cela fonctionne.

J'essaie de parcourir les didacticiels interactifs, mais j'utilise plutôt un ViewModel.

Je ne sais pas encore comment aborder ces parties

function AppViewModel() {
    this.firstName = ko.observable("Bert");
    this.lastName = ko.observable("Bertington");
}

ou

function AppViewModel() {
    // ... leave firstName, lastName, and fullName unchanged here ...

    this.capitalizeLastName = function() {
        var currentVal = this.lastName();        // Read the current value
        this.lastName(currentVal.toUpperCase()); // Write back a modified value
    };


Modifier 2

J'ai pu découvrir le premier problème. Aucune idée du deuxième problème. Pourtant cependant. Quelqu'un a une idée?

 @model MvcApplication1.Models.Test

    @{
        Layout = null;
    }

    <!DOCTYPE html>

    <html>
    <head>
        <title>Index</title>
        <script src="../../Scripts/jquery-1.5.1.js" type="text/javascript"></script>
        <script src="../../Scripts/knockout-2.1.0.js" type="text/javascript"></script>
        <script src="../../Scripts/knockout.mapping-latest.js" type="text/javascript"></script>
       <script type="text/javascript">

       $(function()
       {
        var model = @Html.Raw(Json.Encode(Model));
        var viewModel = ko.mapping.fromJS(model);
        ko.applyBindings(viewModel);

       });

    </script>

    </head>
    <body>
        <div>
            @*grab values from the view model directly*@
            <p>First name: <strong data-bind="text: FirstName"></strong></p>
            <p>Last name: <strong data-bind="text: LastName"></strong></p>

            @*grab values from my second view model that I made*@
            <p>SomeOtherValue <strong data-bind="text: Test2.SomeOtherValue"></strong></p>
            <p>Another <strong data-bind="text: Test2.Another"></strong></p>

            @*allow changes to all the values that should be then sync the above values.*@
            <p>First name: <input data-bind="value: FirstName" /></p>
            <p>Last name: <input data-bind="value: LastName" /></p>
            <p>SomeOtherValue <input data-bind="value: Test2.SomeOtherValue" /></p>
            <p>Another <input data-bind="value: Test2.Another" /></p>

           @* seeing if I can do it with p tags and see if they all update.*@
            <p data-bind="foreach: Test3">
                <strong data-bind="text: Test3Value"></strong> 
            </p>

     @*took my 3rd view model that is in a collection and output all values as a textbox*@       
    <table>
        <thead><tr>
            <th>Test3</th>
        </tr></thead>
          <tbody data-bind="foreach: Test3">
            <tr>
                <td>    
                    <strong data-bind="text: Test3Value"></strong> 
<input type="text" data-bind="value: Test3Value"/>
                </td>
            </tr>    
        </tbody>
    </table>

Manette

  public ActionResult Index()
    {
              Test2 test2 = new Test2
        {
            Another = "test",
            SomeOtherValue = "test2"
        };

        Test vm = new Test
        {
            FirstName = "Bob",
            LastName = "N/A",
             Test2 = test2,

        };
        for (int i = 0; i < 10; i++)
        {
            Test3 test3 = new Test3
            {
                Test3Value = i.ToString()
            };

             vm.Test3.Add(test3);
        }

        return View(vm);
    }
chobo2
la source
2
Je viens d'écrire un article de blog pour répondre à une autre question similaire: roysvork.wordpress.com/2012/12/09/ ... Cela ne répond peut-être pas complètement à votre question, mais cela vous donne une bonne idée de la façon dont les choses pourraient fonctionner. J'espère continuer avec un autre article dans un avenir pas trop lointain. N'hésitez pas à me poser des questions dans les commentaires sur le post ou ici si vous avez besoin de plus d'informations.
au-delà du code

Réponses:

180

Je pense avoir résumé toutes vos questions, si j'ai manqué quelque chose, faites-le moi savoir ( si vous pouviez résumer toutes vos questions en un seul endroit, ce serait bien =))

Remarque. Compatibilité avec le ko.editableplug-in ajouté

Téléchargez le code complet

Comment utiliser les helpers HTML avec knockout.js

C'est facile:

@Html.TextBoxFor(model => model.CourseId, new { data_bind = "value: CourseId" })

Où:

  • value: CourseIdindique que vous liez la valuepropriété du inputcontrôle à la CourseIdpropriété de votre modèle et de votre modèle de script

Le résultat est:

<input data-bind="value: CourseId" data-val="true" data-val-number="The field CourseId must be a number." data-val-required="The CourseId field is required." id="CourseId" name="CourseId" type="text" value="12" />

Pourquoi le document était-il prêt pour le faire fonctionner (voir la première modification pour plus d'informations)

Je ne comprends pas encore pourquoi vous devez utiliser l' readyévénement pour sérialiser le modèle, mais il semble que cela soit simplement nécessaire (ne pas vous en soucier cependant)

Comment faire quelque chose comme ça si j'utilise le mappage knockout avec mes modèles de vue? Comme je n'ai pas de fonction à cause de la cartographie.

Si je comprends bien, vous devez ajouter une nouvelle méthode au modèle KO, eh bien, c'est facile de fusionner des modèles

Pour plus d'informations, dans la section -Cartographie à partir de différentes sources-

function viewModel() {
    this.addStudent = function () {
        alert("de");
    };
};

$(function () {
    var jsonModel = '@Html.Raw(JsonConvert.SerializeObject(this.Model))';
    var mvcModel = ko.mapping.fromJSON(jsonModel);

    var myViewModel = new viewModel();
    var g = ko.mapping.fromJS(myViewModel, mvcModel);

    ko.applyBindings(g);
});

À propos de l'avertissement que vous receviez

Avertissement 1 La compilation conditionnelle est désactivée -> @ Html.Raw

Vous devez utiliser des citations

Compatibilité avec le plug-in ko.editable

Je pensais que cela allait être plus complexe, mais il s'avère que l'intégration est vraiment facile, afin de rendre votre modèle modifiable, ajoutez simplement la ligne suivante: (rappelez-vous que dans ce cas, j'utilise un modèle mixte, à partir du serveur et l'ajout d'une extension dans le client et le modifiable fonctionne simplement ... c'est super):

    ko.editable(g);
    ko.applyBindings(g);

À partir de là, il vous suffit de jouer avec vos liaisons en utilisant les extensions ajoutées par le plug-in, par exemple, j'ai un bouton pour commencer à éditer mes champs comme celui-ci et dans ce bouton, je lance le processus d'édition:

    this.editMode = function () {
        this.isInEditMode(!this.isInEditMode());
        this.beginEdit();
    };

Ensuite, j'ai des boutons de validation et d'annulation avec le code suivant:

    this.executeCommit = function () {
        this.commit();
        this.isInEditMode(false);
    };
    this.executeRollback = function () {
        if (this.hasChanges()) {
            if (confirm("Are you sure you want to discard the changes?")) {
                this.rollback();
                this.isInEditMode(false);
            }
        }
        else {
            this.rollback();
            this.isInEditMode(false);
        }
    };

Et enfin, j'ai un champ pour indiquer si les champs sont en mode édition ou non, c'est juste pour lier la propriété enable.

this.isInEditMode = ko.observable(false);

À propos de votre question sur le tableau

Je pourrais avoir des boucles foreach ou quelque chose pour extraire les données de la collection de modèles de vue étudiant.

Ensuite, lorsque je soumettrais le formulaire, j'utiliserais jquery et sérialiserais le tableau et l'envoyer à une méthode d'action de contrôleur qui le lierait au viewmodel.

Vous pouvez faire de même avec KO, dans l'exemple suivant, je vais créer la sortie suivante:

entrez la description de l'image ici

Fondamentalement, ici, vous avez deux listes, créées à l'aide Helperset liées avec KO, elles ont un dblClickévénement lié qui, lorsqu'il est déclenché, supprime l'élément sélectionné de la liste actuelle et l'ajoute à l'autre liste, lorsque vous publiez sur le Controller, le contenu de chaque la liste est envoyée sous forme de données JSON et ré-attachée au modèle de serveur

Nuggets:

Scripts externes .

Code contrôleur

    [HttpGet]
    public ActionResult Index()
    {
        var m = new CourseVM { CourseId = 12, CourseName = ".Net" };

        m.StudentViewModels.Add(new StudentVm { ID = 545, Name = "Name from server", Lastname = "last name from server" });

        return View(m);
    }

    [HttpPost]
    public ActionResult Index(CourseVM model)
    {
        if (!string.IsNullOrWhiteSpace(model.StudentsSerialized))
        {
            model.StudentViewModels = JsonConvert.DeserializeObject<List<StudentVm>>(model.StudentsSerialized);
            model.StudentsSerialized = string.Empty;
        }

        if (!string.IsNullOrWhiteSpace(model.SelectedStudentsSerialized))
        {
            model.SelectedStudents = JsonConvert.DeserializeObject<List<StudentVm>>(model.SelectedStudentsSerialized);
            model.SelectedStudentsSerialized = string.Empty;
        }

        return View(model);
    }

Modèle

public class CourseVM
{
    public CourseVM()
    {
        this.StudentViewModels = new List<StudentVm>();
        this.SelectedStudents = new List<StudentVm>();
    }

    public int CourseId { get; set; }

    [Required(ErrorMessage = "Course name is required")]
    [StringLength(100, ErrorMessage = "Course name cannot be this long.")]
    public string CourseName { get; set; }

    public List<StudentVm> StudentViewModels { get; set; }
    public List<StudentVm> SelectedStudents { get; set; }

    public string StudentsSerialized { get; set; }
    public string SelectedStudentsSerialized { get; set; }
}

public class StudentVm
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Lastname { get; set; }
}

Page CSHTML

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>CourseVM</legend>

        <div>
            <div class="editor-label">
                @Html.LabelFor(model => model.CourseId)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.CourseId, new { data_bind = "enable: isInEditMode, value: CourseId" })
                @Html.ValidationMessageFor(model => model.CourseId)
            </div>

            <div class="editor-label">
                @Html.LabelFor(model => model.CourseName)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(model => model.CourseName, new { data_bind = "enable: isInEditMode, value: CourseName" })
                @Html.ValidationMessageFor(model => model.CourseName)
            </div>
            <div class="editor-label">
                @Html.LabelFor(model => model.StudentViewModels);
            </div>
            <div class="editor-field">

                @Html.ListBoxFor(
                    model => model.StudentViewModels,
                    new SelectList(this.Model.StudentViewModels, "ID", "Name"),
                    new
                    {
                        style = "width: 37%;",
                        data_bind = "enable: isInEditMode, options: StudentViewModels, optionsText: 'Name', value: leftStudentSelected, event: { dblclick: moveFromLeftToRight }"
                    }
                )
                @Html.ListBoxFor(
                    model => model.SelectedStudents,
                    new SelectList(this.Model.SelectedStudents, "ID", "Name"),
                    new
                    {
                        style = "width: 37%;",
                        data_bind = "enable: isInEditMode, options: SelectedStudents, optionsText: 'Name', value: rightStudentSelected, event: { dblclick: moveFromRightToLeft }"
                    }
                )
            </div>

            @Html.HiddenFor(model => model.CourseId, new { data_bind="value: CourseId" })
            @Html.HiddenFor(model => model.CourseName, new { data_bind="value: CourseName" })
            @Html.HiddenFor(model => model.StudentsSerialized, new { data_bind = "value: StudentsSerialized" })
            @Html.HiddenFor(model => model.SelectedStudentsSerialized, new { data_bind = "value: SelectedStudentsSerialized" })
        </div>

        <p>
            <input type="submit" value="Save" data-bind="enable: !isInEditMode()" /> 
            <button data-bind="enable: !isInEditMode(), click: editMode">Edit mode</button><br />
            <div>
                <button data-bind="enable: isInEditMode, click: addStudent">Add Student</button>
                <button data-bind="enable: hasChanges, click: executeCommit">Commit</button>
                <button data-bind="enable: isInEditMode, click: executeRollback">Cancel</button>
            </div>
        </p>
    </fieldset>
}

Les scripts

<script src="@Url.Content("~/Scripts/jquery-1.7.2.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/knockout.mapping-latest.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/ko.editables.js")" type="text/javascript"></script>

<script type="text/javascript">
    var g = null;
    function ViewModel() {
        this.addStudent = function () {
            this.StudentViewModels.push(new Student(25, "my name" + new Date(), "my last name"));
            this.serializeLists();
        };
        this.serializeLists = function () {
            this.StudentsSerialized(ko.toJSON(this.StudentViewModels));
            this.SelectedStudentsSerialized(ko.toJSON(this.SelectedStudents));
        };
        this.leftStudentSelected = ko.observable();
        this.rightStudentSelected = ko.observable();
        this.moveFromLeftToRight = function () {
            this.SelectedStudents.push(this.leftStudentSelected());
            this.StudentViewModels.remove(this.leftStudentSelected());
            this.serializeLists();
        };
        this.moveFromRightToLeft = function () {
            this.StudentViewModels.push(this.rightStudentSelected());
            this.SelectedStudents.remove(this.rightStudentSelected());
            this.serializeLists();
        };
        this.isInEditMode = ko.observable(false);
        this.executeCommit = function () {
            this.commit();
            this.isInEditMode(false);
        };
        this.executeRollback = function () {
            if (this.hasChanges()) {
                if (confirm("Are you sure you want to discard the changes?")) {
                    this.rollback();
                    this.isInEditMode(false);
                }
            }
            else {
                this.rollback();
                this.isInEditMode(false);
            }
        };
        this.editMode = function () {
            this.isInEditMode(!this.isInEditMode());
            this.beginEdit();
        };
    }

    function Student(id, name, lastName) {
        this.ID = id;
        this.Name = name;
        this.LastName = lastName;
    }

    $(function () {
        var jsonModel = '@Html.Raw(JsonConvert.SerializeObject(this.Model))';
        var mvcModel = ko.mapping.fromJSON(jsonModel);

        var myViewModel = new ViewModel();
        g = ko.mapping.fromJS(myViewModel, mvcModel);

        g.StudentsSerialized(ko.toJSON(g.StudentViewModels));
        g.SelectedStudentsSerialized(ko.toJSON(g.SelectedStudents));

        ko.editable(g);
        ko.applyBindings(g);
    });
</script>

Remarque: je viens d'ajouter ces lignes:

        @Html.HiddenFor(model => model.CourseId, new { data_bind="value: CourseId" })
        @Html.HiddenFor(model => model.CourseName, new { data_bind="value: CourseName" })

Parce que lorsque je soumets le formulaire, mes champs sont désactivés, donc les valeurs n'ont pas été transmises au serveur, c'est pourquoi j'ai ajouté quelques champs cachés pour faire l'affaire

Jupaol
la source
Hmm très instructif. D'après votre réponse et la réponse de Pual, je pense que j'ai presque obtenu une réponse à toutes mes questions, sauf comment utiliser des plugins comme modifiables. J'espère que quelqu'un sait comment je peux utiliser cela.
chobo2
1
Je viens d'ajouter la compatibilité avec le ko.editablesplug-in, vous pouvez vérifier la réponse mise à jour ou si vous le souhaitez, vous pouvez télécharger l'ensemble du projet pour l'exécuter localement
Jupaol
Je vérifierai quand je le pourrai. Est-ce que beaucoup de choses ont dû changer pour que cela fonctionne? Je me demande si pour chaque plugin je trouve si je devrai y apporter des modifications et ensuite garder ma propre version.
chobo2
Nan. Vous serez surpris, c'est presque hors de la boîte
Jupaol
1
Merci un homme de bande, j'ai appris plusieurs nouvelles stratégies de votre réponse. Gloire!
sky-dev
23

Vous pouvez sérialiser votre modèle de vue ASP.NET MVC dans une variable javascript:

@model CourseVM
<script type="text/javascript">
    var model = @Html.Raw(Json.Encode(Model));
    // go ahead and use the model javascript variable to bind with ko
</script>

Il y a beaucoup d'exemples dans la documentation de KO que vous pourriez parcourir.

Darin Dimitrov
la source
1
Oui, j'ai parcouru le tutoriel interactif qu'ils ont sur le site mais je ne vois vraiment rien à voir avec asp.net mvc. Je vois qu'ils ont aussi un plugin de mappage mais je ne sais pas comment cela s'intègre. Dans votre exemple, comment le lieriez-vous au modèle knockout (dans un autre script). Je veux vraiment avoir le moins de javascript en ligne possible (non est préférable mais je suppose que ce n'est pas possible)
chobo2
2
Quel problème essayez-vous de résoudre? Si vous voulez des vues MVC et que vous êtes satisfait de la façon de les utiliser, vous pouvez vous y tenir. Si vous voulez une liaison et une manipulation de données côté client, KO est un excellent choix. Vous pouvez générer votre modèle de vue KO à partir de votre code MVC comme le montre cette réponse. Il prend le vm et le sérialise en json. Ensuite, sur le client, vous pouvez mapper les résultats vers un modèle de vue javascript. Ensuite, liez le modèle de vue à la vue et vous êtes prêt. La clé est que MVC et KO ne doivent en aucun cas être couplés, à moins que vous ne le souhaitiez. Tout dépend du problème que vous essayez de résoudre.
John Papa
1
Il est normal que vous ne voyiez rien à voir avec asp.net mvc. Knockout est un framework côté client. Il ne sait ni ne se soucie de la langue que vous utilisez côté serveur. Ces 2 cadres doivent être absolument découplés.
Darin Dimitrov
@JohnPapa - J'aime la façon dont je fais les choses maintenant mais j'aime aussi apprendre de nouvelles choses (je vois que KO peut être très utile dans certaines situations). Je sais que KO est un script côté client, mais pour moi, je les vois comme travaillant ensemble. Je génère actuellement mes vues en utilisant des modèles de vue et des helpers html. Donc, dans mon esprit, KO doit travailler avec cela. Par exemple, disons que vous avez une boîte de dialogue d'édition. Comment allez-vous concevoir et remplir les valeurs d'une base de données dans ces champs. Si j'utilisais ma façon, ce serait une vue des helpers html qui a un viewModel. Remplirait le modèle de vue et l'envoyait via la méthode d'action et l'utiliserait.
chobo2
1
@ chobo2, knockout est un framework côté client. Il utilise des modèles de vue sur le client pour implémenter le modèle MVC sur le client. Le serveur est découplé. Vous pouvez également utiliser des modèles de vue dessus. C'est juste 2 endroits différents. Si vous avez une logique complexe que vous souhaitez implémenter sur le client en utilisant javascript, alors knockout pourrait simplifier cela. Sinon, honnêtement, vous n'en avez pas besoin.
Darin Dimitrov
2

Pour obtenir les propriétés calculées supplémentaires après le mappage du serveur, vous devrez améliorer davantage vos modèles de vue côté client.

Par exemple:

var viewModel = ko.mapping.fromJS(model);

viewModel.capitalizedName = ko.computed(function() {...}, viewModel);

Ainsi, chaque fois que vous mappez à partir du JSON brut, vous devez réappliquer les propriétés calculées.

De plus, le plugin de mappage offre la possibilité de mettre à jour de manière incrémentielle un modèle de vue au lieu de le recréer à chaque fois que vous allez et vient (utilisez un paramètre supplémentaire dans fromJS):

// Every time data is received from the server:
ko.mapping.fromJS(data, viewModel);

Et cela exécute une mise à jour incrémentielle des données sur votre modèle des seules propriétés mappées. Vous pouvez en savoir plus à ce sujet dans la documentation cartographique

Vous avez mentionné dans les commentaires sur la réponse de Darin le package FluentJSON . J'en suis l'auteur, mais son cas d'utilisation est plus spécifique que ko.mapping. Je ne l'utiliserais généralement que si vos viewmodels sont à sens unique (c'est-à-dire serveur -> client) et que les données sont ensuite renvoyées dans un format différent (ou pas du tout). Ou si votre modèle de vue javascript doit être dans un format sensiblement différent de votre modèle de serveur.

Paul Tyng
la source
Hmm, alors je suppose que FluentJSON n'est peut-être pas pour moi car mes viewmodels vont la plupart du temps dans les deux sens (je le renvoie généralement via json, puis je le lie au viewmodel dans le paramètre de méthode d'action). Savez-vous comment je pourrais utiliser ces plugins que j'ai mentionnés comme modifiables? Enfin, est-ce que je perds des fonctionnalités en utilisant la cartographie et en essayant d'utiliser mon viewmodel plutôt que de ne pas l'utiliser?
chobo2
Je n'ai utilisé aucun des plugins donc pas sûr. Ce que j'ai fait dans le passé, c'était simplement de m'abonner à chaque changement et de conserver une pile d'états de modèle de vue sérialisés sur lesquels je pousserais en cas de changement et que je ferais une annulation ( voir cette question ).
Paul Tyng
Le mappage ne vous empêche pas de toute fonctionnalité, il vous suffit de vous assurer et de respecter ses conventions sur la façon dont il gère le mappage vers et depuis JS pour que tout fonctionne bien ensemble.
Paul Tyng
Eh bien, la réponse acceptée à la question que vous avez postée est essentiellement ce que serait le plugin. C'est ce qui me trouble car vous pouvez voir qu'ils créent un viewmodel et utilisent ensuite leur fonction qu'ils ont créée (ko.observableArrayWithUndo ([])). Si je fais de la cartographie, je ne sais pas comment faire cela. La seule chose qui me vient à l'esprit est d'écrire mon propre mappage (ce que je doute pouvoir faire pour le moment) qui permette d'annuler l'observable ou de mapper chaque propriété, mais j'ai essentiellement des modèles de vue en double, un pour le serveur et un pour le client et je suis peur qui deviendra impossible à maintenir
chobo2
Ah ouais désolé je parlais de ma réponse à cette question, désolé aurait dû être lié directement.
Paul Tyng