web-dev-qa-db-ja.com

マッピングプラグインによって作成されたノックアウトオブジェクトのディープコピーを作成する方法

これが私のシナリオです。ノックアウトマッピングプラグインを使用して、監視可能なビューモデル階層を作成しています。私の階層にはネストされた要素があります。階層の特定の時点で、[追加]ボタンを配置して、その要素の新しい空のコピーをobservablearrayに挿入します。問題は、whateverArray.Push(new MyObject())とは言えないことです。

マッピングプラグインが実際に階層全体を作成したため、「MyObject」にアクセスできません。したがって、新しいアイテムを挿入するために私ができる唯一のことは、前のアイテムを見てそれをコピーすることです。 ko.utils.extend関数を試しましたが、実際のクローンを作成しているようには見えません。オブジェクトが返されますが、そのオブジェクトを更新しても、コピー元の元のオブジェクトには影響します。

Jsfiddleを参照

22
emirhosseini

これをマッピング設定で設定する方法があるかもしれませんが、私はまだそれを完全に理解することができません。

当面は、オブジェクトのマッピングを解除して元に戻し、基本的にコピーを作成します。

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

これは、他のライブラリと同じようにそれを行う最も簡単な方法です。


私は、マッピングオプションを使用してこれを行う素敵な方法を探していましたが、方法を見つけました。

デフォルトでは、マッピングプラグインはソースオブジェクトから監視可能なインスタンスを取得し、ターゲットオブジェクトで同じインスタンスを使用します。したがって、実際には、両方のインスタンスが同じオブザーバブルを共有します(バグ?)。私たちがする必要があるのは、各プロパティの新しいオブザーバブルを作成し、値をコピーすることです。

幸い、オブジェクトの各プロパティをマップする便利なユーティリティ関数があります。次に、値のコピーで初期化された新しい監視可能なインスタンスを作成できます。

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

これは浅いコピーになることに注意してください。深いコピーが必要な場合は、オブジェクトを再帰的にマッピングする必要があります。これはあなたの例の問題を修正します。

38
Jeff Mercado
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;
};

これが私の解決策です、それが役に立てば幸いです。このコードでは、objがviewModelオブジェクトになります。

7
Teddy