22 votes

Comment faire une copie profonde d'un objet knockout qui a été créé par le plugin mapping ?

Voici mon scénario. J'utilise le plugin knockout mapping pour créer une hiérarchie de modèles de vues observables. Ma hiérarchie contient des éléments imbriqués. À un point particulier de la hiérarchie, je veux placer un bouton Ajouter pour insérer une nouvelle copie vierge de cet élément dans l'observablearray. Le problème est que je ne peux pas simplement dire whateverArray.push(new MyObject()).

Comme le plugin de mappage a créé toute la hiérarchie pour moi, je n'ai pas accès à "MyObject". Il semble donc que la seule chose que je puisse faire pour insérer un nouvel élément soit de regarder un élément précédent et de le copier. J'ai essayé la fonction ko.utils.extend, mais elle ne semble pas créer un clone réel. Elle me rend un objet, mais lorsque je mets à jour cet objet, cela affecte toujours l'objet original à partir duquel il a été copié.

Voir jsfiddle exemple

39voto

Jeff Mercado Points 42075

Il y a peut-être un moyen de configurer cela dans les paramètres de cartographie, mais je n'arrive pas encore à le savoir.

Dans l'intervalle, vous pouvez simplement démapper l'objet et le remapper, ce qui revient à faire une copie.

var newJob = ko.mapping.fromJS(ko.mapping.toJS(job));

Ce sera le moyen le plus simple de le faire comme n'importe quelle autre bibliothèque, "désérialiser" et "sérialiser" à nouveau.


J'ai cherché une façon agréable de le faire en utilisant les options de cartographie et j'ai trouvé un moyen.

Par défaut, le plugin de mapping prendra les instances d'observables de l'objet source et utilisera la même instance dans l'objet cible. Donc, en fait, les deux instances partageront les mêmes observables (bug ?). Ce que nous devions faire était de créer une nouvelle observable pour chaque propriété et de copier les valeurs.

Heureusement, il existe une fonction utilitaire très pratique pour cartographier chacune des propriétés d'un objet. Nous pouvons alors créer nos nouvelles instances observables initialisées avec des copies des valeurs.

// Deep copy
var options = {
    create: function (options) {
        // map each of the properties
        return ko.mapping.visitModel(options.data, function (value) {
            // create new instances of observables initialized to the same value
            if (ko.isObservable(value)) { // may want to handle more cases
                return ko.observable(value);
            }
            return value;
        });
    }
};
var newJob = ko.mapping.fromJS(job, options);

Notez qu'il s'agira d'une copie superficielle, vous devrez probablement mapper récursivement les objets si vous voulez une copie profonde. Cependant, cela permettra de résoudre le problème dans votre exemple.

7voto

Teddy Points 347
ko.utils.clone = function (obj) {
    var target = new obj.constructor();
    for (var prop in obj) {
        var propVal = obj[prop];
        if (ko.isObservable(propVal)) {
            var val = propVal();
            if ($.type(val) == 'object') {
                target[prop] = ko.utils.clone(val);
                continue;
            }
            target[prop](val);
        }
    }
    return target;
};

Voici ma solution, j'espère qu'elle vous aidera. Dans ce code, obj serait votre objet viewModel.

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