Подтвердить что ты не робот

Как сделать глубокую копию объекта нокаута, который был создан плагином сопоставления

Вот мой сценарий. Я использую плагин отображения нокаута для создания наблюдаемой иерархии viewmodel для меня. В моей иерархии есть вложенные элементы. В определенной точке иерархии я хочу добавить кнопку Добавить, чтобы вставить новую пустую копию этого элемента в наблюдаемый символ. Проблема в том, что я не могу просто сказать anyArray.push(новый MyObject()).

Поскольку плагин сопоставления фактически создал для меня всю иерархию, у меня нет доступа к "MyObject". Поэтому, кажется, единственное, что я могу сделать, чтобы вставить новый элемент, - это посмотреть на предыдущий элемент и скопировать его. Я попробовал функцию ko.utils.extend, но это, похоже, не делает фактический клон. Это возвращает мне объект, но когда я обновляю этот объект, он все равно влияет на исходный объект, из которого он был скопирован.

См. jsfiddle пример

4b9b3361

Ответ 1

Возможно, есть способ установить это в настройках сопоставления, но я пока не могу это понять.

В то же время вы можете просто отменить привязку объекта и отобразить его обратно, чтобы вы делали копию.

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

Обратите внимание, что это будет мелкая копия, вам, вероятно, придется рекурсивно отображать объекты, если вы хотите получить глубокую копию. Однако это устранит проблему в вашем примере.

Ответ 2

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.