Кросс-обновление проблемы с нокаутом

У меня проблема с перекрестным обновлением нокаута. Следующая функция ('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="&lt;add a tag&gt;" 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 Вы нажимаете на это), то вам нужно чисто компьютерное..

Другие вопросы по тегам