淘汰赛的交叉更新问题

Cross-updating issue with knockout

我有一个 "cross-update" 敲除问题。以下函数 ('self.applyTagsAllocation') 会导致一些奇怪的效果。第 3 行:'scenetag.sceneID = scene.sceneID;' 正在更新 'tag.sceneID' 以及本地场景标签变量。我真的不明白为什么 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-array 和一个 tags-array。这是因为我在这个例子中做了一个简化。在我的项目中我有另一个标签类型和两种类型在视图模型范围内合并在一起。)

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 是可排序排序中的一个循环(来自 Ryan Niemeyer):

<div class="tag-list" data-bind="sortable:{template:'tagsTmpl',data:$root.sceneTags"></div>

这可能是什么原因造成的? 非常感谢任何提示和线索!

提前致谢!

再看看函数;在您看来,您将标签的 $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 中您将其推送给它),那么您需要 pureComputed..