2015-01-14 3 views
0

У меня проблема с перекрестным обновлением с нокаутом. Следующая функция («self.applyTagsAllocation») вызывает некоторый странный эффект. Строка 3: 'scenetag.sceneID = scene.sceneID;' обновляет «tag.sceneID», а также локальную переменную scenetag. Я действительно не могу понять, почему, поскольку сценарий никогда не является наблюдаемым.Проблема с перекрестным обновлением с нокаутом

self.applyTagsAllocation = function (tag) { 
     var scenetag = tag; 
     var scene = self.selectedScene(); 
     scenetag.sceneID = scene.sceneID; 
     scene.sceneTags.push(scenetag); 
}; 

Далее мы приводим более полный перечень моих ViewModel:

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

(Вы можете спросить, почему «Сцена» имеет как sceneTags-массив и теги-массив Это потому, что я сделал. .. упрощение в этом примере в моем проекте у меня есть еще один тег типа и два типа объединяется в ViewModel-Scope)

HTML

Tag сначала устанавливаются с:

<input class="taginput" placeholder="&lt;add a tag&gt;" data-bind="textInput: tagname, event: {keyup: $root.inputTag}" /> 

Тогда, когда тег существует, я хочу, чтобы добавить его на другую «сцену» из сферы «Еогеасп: тег» с:

<span data-bind="visible: sceneID !== null && sceneID !== $root.selectedScene().sceneID, click: function(){$root.applyTagsAllocation($data)};">Add Tag</span> 

Фактическое Еогеасп здесь есть цикл в knockout- (от Райана Нимейера):

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

Что может быть причиной этого? Любые подсказки и подсказки высоко оценены!

Thanx заранее!

ответ

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 вы нажимаете на него), тогда вам нужно pureComputed.

+0

Hi @Tyblitz! Большое спасибо за ваш ответ. Ваше предложение с клонированием, похоже, делает трюк. Brilliant. Я так благодарен! :) –

+0

Просто подумал о другом возможном подходе: вместо клонирования я мог бы использовать «тег» для создания нового тега-объекта с теми же атрибутами, кроме self.selectedScene(). SceneID, не так ли? Тогда я, вероятно, должен переписать «self.addSceneTag», чтобы включить обе функции. –

+1

@AsleG, что вы могли бы сделать; В вашем конструкторе вы можете сделать var var = function (data, flag) {self.sceneID = flag === true? data.sceneID || ''; 'Если вы не хотите включать его в конструктор, вы можете создать новый тег со всеми реквизитами' var x = new Tag (data); 'и повторно назначить идентификатор сцены после этого, например' x.sceneID = '' 'или если вы хотите полностью удалить опору' delete x.sceneID'. – Tyblitz

Смежные вопросы