Кросс-обновление проблемы с нокаутом
У меня проблема с перекрестным обновлением нокаута. Следующая функция ('self.applyTagsAllocation') вызывает странный эффект. Строка 3: 'scenetag.sceneID = scene.sceneID;' обновляет tag.sceneID, а также локальную переменную scenetag. Я действительно не могу понять, почему, поскольку sceneID никогда не является наблюдаемым.
self.applyTagsAllocation = function (tag) {
var scenetag = tag;
var scene = self.selectedScene();
scenetag.sceneID = scene.sceneID;
scene.sceneTags.push(scenetag);
};
Вот более полный список моей модели представления:
var Tag = function (data) {
var self = this;
self.ID = data.ID || -1;
self.sceneID = data.sceneID;
self.text = ko.observable((data.text || 'new').trim());
self.tagType = ko.observable(data.tagType || -1 );
}
var Scene = function(data){
var self = this;
self.sceneID = data.sceneID;
self.sceneTags = ko.observableArray();
self.tags = ko.computed({
read: function () {
var tags = [];
tags.push.apply(tags, self.sceneTags());
return tags;
},
write: function (tag) {
this.sceneTags.push(tag);
},
owner: self
});
};
var ViewModel = function (model){
var self = this;
self.selectedScene = ko.observable();
self.sceneTags = ko.observableArray();
self.Scenes = ko.observableArray(
ko.utils.arrayMap(model, function (item) {
return new Scene(item);
}));
self.sceneTags = ko.computed(function () {
var tags = [];
ko.utils.arrayForEach(self.Scenes(), function (scene) {
tags.push.apply(tags, scene.tags());
});
return tags;
});
//Tag is first created with:
self.addSceneTag = function (name, type) {
if (!type) type = -1;
var newtag = new Tag({
ID: -1,
sceneID: self.selectedScene().sceneID,
text: name,
tagType: type
});
// already in use?
var abort = false;
ko.utils.arrayForEach(self.selectedScene().sceneTags(), function (tag) {
if (tag.text() === newtag.text()) abort = true;
});
if (!abort) self.selectedScene().sceneTags.push(newtag);
};
self.applyTagsAllocation = function (tag) {
var scenetag = tag;
var scene = self.selectedScene();
scenetag.sceneID = scene.sceneID;
scene.sceneTags.push(scenetag);
};
};
(Вы можете спросить, почему у 'Scene' есть и массив sceneTags, и массив тегов. Это потому, что я сделал упрощение в этом примере. В моем проекте у меня есть другой тип тега, и оба типа объединены в ViewModel-сферу.)
HTML
Сначала тег устанавливается с:
<input class="taginput" placeholder="<add a tag>" data-bind="textInput: tagname, event: {keyup: $root.inputTag}" />
Затем, когда тег существует, я хочу добавить его в другую "сцену" из области "foreach: tag" с помощью:
<span data-bind="visible: sceneID !== null && sceneID !== $root.selectedScene().sceneID, click: function(){$root.applyTagsAllocation($data)};">Add Tag</span>
Фактический foreach здесь - это цикл внутри сортировки с выбыванием (от Райана Нимейера):
<div class="tag-list" data-bind="sortable:{template:'tagsTmpl',data:$root.sceneTags"></div>
Что может быть причиной этого? Любые намеки и подсказки высоко ценятся!
Спасибо заранее!
1 ответ
Посмотрите еще раз на функцию; по вашему мнению, вы связываете $data
для тега к self.applyTagsAllocation
функция, которая по сути делает это =>
self.applyTagsAllocation = function (tag) {
var scenetag = tag; // scenetag == instance of Tag you passed ( $data )
var scene = self.selectedScene(); // scene == the selected instance of Tag
scenetag.sceneID = scene.sceneID; //!!!! This line says:
// 'Make sceneID of this instance = sceneID of the selected instance'
scene.sceneTags.push(scenetag); // will fail, you can't write to a simple computed
};
Так что да, он обновляет локальную переменную, но локальная переменная указывает на ваш Tag
viewModel, и поэтому он обновляет объект. В отличие от того, что вы, возможно, думали (казалось, думали), назначение объекта другой переменной не клонирует его; это просто ссылка.
Скорее делайте:
self.applyTagsAllocation = function (tag) {
var scenetag = {};
// clone an object, for whatever strange reason one could have:
for (var prop in tag) {
scenetag[prop] = tag[prop];
} // now you have a copy of the tag instance you passed [..] rest of function
};
Или, если вы хотите установить тег как выбранный, что я считаю более логичным:self.selectedScene(tag)
в вашей функции.
Важно: вы назначили self.sceneTags
дважды, а во второй раз это была вычисляемая наблюдаемая. Вы не можете писать в вычисляемые наблюдаемые (в то время как в applyTagsAllocation
Вы нажимаете на это), то вам нужно чисто компьютерное..