Проблема здесь в том, что вы используете плагин сопоставления неправильно, и что ваши тестовые данные не имеют смысла.
Всегда будет «предыдущее» значение, когда вы записываете новое значение в точно такая же наблюдаемая .Но плагин сопоставления отбросит все ваши видовые модели и создаст новые, когда вы отобразите совершенно другой набор данных.
Как узнать, что объект с именем "Джон" в первом раундеПредполагается, что это тот же человек, который имеет имя «Питер» во втором туре?Не можетТаким образом, он выбрасывает все контакты, включая все их номера телефонов, и создает новые.В этом сценарии никогда не бывает «предыдущего» значения.
Вам нужно:
- Дайте контактам и телефонным номерам ключ, чтобы их можно было идентифицировать как один и тот же объект по всемувызывает
ko.mapping.fromJS
. - Сообщите подключаемому плагину, какое из свойств объекта должно быть ключевым, добавив функцию
key
в конфигурацию отображения.
Чтение документация по подключаемому модулю - прочитайте полностью, для начала это не так уж и много.
В следующем примере я использовал name
в качестве ключа для контактов и phoneType
в качестве ключа для телефонов, и я изменил тестовые данные, чтобы они имели одинаковые имена и типы телефонов в обоих наборах.Возможно, вы захотите использовать идентификационный номер контакта в качестве ключа вместо имени.
Преимущество использования функции key
состоит в том, что нокаут только обновляет текст телефонного номера в DOM, а не выбрасываети воссоздание всего <li>
и всего в нем, потому что он может распознавать существующие экземпляры модели представления и сохранять их.Это сократит время рендеринга.
/* global ko, $ */
function debug(s) {
$("#log").append('<br>' + s);
}
function PhoneNumber(data) {
var self = this;
self.phoneType = ko.observable();
self.phoneNumber = ko.observable();
self.phoneNumber.subscribe(function(newValue) {
debug('new value: ' + newValue);
});
self.phoneNumber.subscribe(function(previousValue) {
debug('previous value: ' + previousValue);
}, self, "beforeChange");
ko.mapping.fromJS(data, PhoneNumber.mapping, self);
}
PhoneNumber.mapping = {};
function Contact(data) {
var self = this;
self.name = ko.observable();
self.email = ko.observable();
self.phones = ko.observableArray();
ko.mapping.fromJS(data, Contact.mapping, self);
}
Contact.mapping = {
phones: {
create: function(options) {
return new PhoneNumber(options.data);
},
key: function (data) {
return ko.unwrap(data.phoneType);
}
}
};
function PhoneBook(data) {
var self = this;
self.contacts = ko.observableArray();
ko.mapping.fromJS(data, PhoneBook.mapping, self);
}
PhoneBook.mapping = {
contacts: {
create: function(options) {
return new Contact(options.data);
},
key: function (data) {
return ko.unwrap(data.name);
}
}
};
var phoneBookData = {
contacts: [{
name: 'John',
email: 'john@domain.com',
phones: [{
phoneType: 'Home Phone',
phoneNumber: '999-888-777-old'
}, {
phoneType: 'Business Phone',
phoneNumber: '444-888-777-old'
}]
},
{
name: 'Peter',
email: 'peter@domain.com',
phones: [{
phoneType: 'Home Phone',
phoneNumber: '22999-888-777-old'
}, {
phoneType: 'Business Phone',
phoneNumber: '444-888-777-old'
}]
}
]
};
var phoneBookDataOther = {
contacts: [{
name: 'John',
email: 'john@domain.com',
phones: [{
phoneType: 'Home Phone',
phoneNumber: '999-888-777-new'
}, {
phoneType: 'Business Phone',
phoneNumber: '444-888-777-new'
}]
},
{
name: 'Peter',
email: 'peter@domain.com',
phones: [{
phoneType: 'Home Phone',
phoneNumber: '22999-888-777-new'
}, {
phoneType: 'Business Phone',
phoneNumber: '444-888-777-new'
}]
}
]
};
$(document).ready(function() {
var phoneBook = new PhoneBook(phoneBookData);
ko.applyBindings(phoneBook);
debug('<hr>');
setTimeout(function dofunc() {
ko.mapping.fromJS(phoneBookDataOther, phoneBook);
}, 3000);
});
#log { font-family: monospace; font-size: small; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<ul data-bind="foreach: contacts">
<li>
<div data-bind="text: name"></div>
<div data-bind="text: email"></div>
<ul data-bind="foreach: phones">
<li>
<span data-bind="text: phoneType"></span>:
<span data-bind="text: phoneNumber"></span>
</li>
</ul>
</li>
</ul>
<hr>
<div id="log"></div>