2015-10-16 65 views
0

当我对可观察数组执行push或pop操作时,它会反映在ui中。然而,阵列上的其他操作不会改变UI中的任何内容。这里是我的情况的一个例子:KnockoutJS - UI不会使用内置的observableArray方法更新,除了push和pop

<ul data-bind="foreach: addresses"> 
    <!-- ko template: {name: 'AddressItemTemplate', data: {address: $data, page: 'update-page'} }--> 
    <!-- /ko --> 
</ul> 

我用我的模板,在两个不同的页面和那我使用这样的模板数据的原因。

<script type="text/html" id="AddressItemTemplate"> 
    <p data-bind="text: (page == 'update-page') ? 'updating' : 'declined'"</p> 
    <p data-bind="text: address.title"></p> 
</script> 

现在的JS侧,OFC我宣布地址作为可观察到的阵列

this.addresses = ko.observableArray([addresObject1, addressObject2, ...]) 

某处在页面上,我编辑的地址值。有UI反映了变化,我做了以下内容:

//suppose we know that the first address is being edited 
var tmp_addresses = addresses(); 
tmp_addresses[0].title = 'blabla'; 
addresses(tmp_addresses); 

它就在那里,在视图模型,我可以看到addresses的内容已经更新,而不是在用户界面?

addresses.push(someAddressObject); 

addresses.pop(); 

作品(更新与新的/除去元件的UI)。但addresses.splice(0, 1, newAddressObject)不会再次在UI中执行任何操作。

我在这里错过了什么?如何推动流行工作,而不是其他人? 我是否遇到了淘汰赛框架中的错误?

UPDATE

我发现了一种方法来做到这一点,但这里有些不对劲。我会来,但首先:

我很清楚,如果我在observable数组中使用可观察对象,更改将反映在用户界面中。然而这正是我想要避免的事情。这是一个矫枉过正的问题。

在属性真正暴露给用户交互的情况下,应该要求可观察属性。例如,如果您有一个用于设置对象的每个字段的UI,那么是的,可观察属性将是正确的调用。

但在我的情况下,我甚至没有用于更新地址字段的UI。此外,我不需要修改,并不断观察所有地址的所有属性。在我的情况下,每隔一段时间从服务器进行一次更新,并且仅更改单个地址字段中的单个字段。

在另一个角度我建议的方式应该工作。我只是简单地更新整个数组,而不是每个元素都单独更新。这是完全一样的逻辑:

someObservableObject({newObject: withNewFields, ...}); 

这就是为什么我不需要我的对象作为observables。我只是想重新声明这个数组并且完成这个改变。例如,建议如果您要大量推入可观察数组,不要多次使用array.push(...),而是重新声明更大的数组到可观察数组变量我在我的问题中采用了类似的方式。否则,我告诉淘汰赛跟踪他们中的每一个单个对象和每一个领域,这不是我想要的。

现在,我终于得到它的工作但我的方式表明,有一个更干净的方式来做到这一点。

我发现,当您重新声明数组时,observable数组中的项目会以某种方式跟踪并且不会更新。例如,我在问题中给出的代码不起作用。然而,下面的代码工作:

var tmp_addresses = addresses(); 
var tmp_addr = tmp_addresses[0]; 
var new_addr = {}; 
Object.keys(tmp_addr).forEach(function(key){ 
    new_addr[key] = tmp_addr[key]; 
}); 
new_addr.title = 'Hey this is something new!' 
addresses.splice(0, 1, new_addr); 

不满意?下面的代码是去工作为好,因为我们正在重新定义数组:

var newAddressObject1 = {...}, newAddressObject2 = {...}; 
addresses([newAddressObject1, newAddressObject2]); 

但是下面是行不通的!

var tmp_addresses = addresses(); 
var tmp_addr = tmp_addresses[0]; 
tmp_addr.title = 'Hey this address wont update'; 
addresses.splice(0, 1, tmp_addr); 

怎么回事?我认为knockout在observableArrays中添加了一个内部属性给他的项目,当我尝试重新插入它时,它不会更新。

我的问题现在已经演变为创建一个new对象与可观察数组中的所需项目的相同属性。我上面编码的方式非常肮脏。有一个更好的方法可以做到这一点

+1

我相信你的答案在这里:http://stackoverflow.com/questions/14953248/how-to-update-observable-array-element-in-knockoutjs – ryuu9187

+0

数组是观察,而不是个别元素。 – ryuu9187

+0

@ jason9187我没有更新单个元素,我正在更新整个数组。请查看我在 – batilc

回答

0

的问题是完全一样@ jason9187在评论中指出:物体的观察到阵列中的引用不,当我编辑改变他们的一个领域。因此,KO不会将我的数组解释为已更改。如果observableArray包含简单的数据类型,那么我建议的方式可以毫无问题地工作。但是,我在数组中有一个对象,因此虽然我编辑了对象,但它的引用(指针)保持不变,并且KO认为所有对象都与以前相同。

为了达到我想要的效果,我们必须解决深层克隆问题在JavaScript中的问题,如in this post

现在有一个折衷,如果在对象中没有循环体系结构或功能,深层克隆在香草中非常简单。就我而言,没有这样的事情。数据来自一个平静的API。如果将来有人得到这个问题,他们需要深入克隆他们的“难克隆”对象。

这里是我的解决方案:

var tmp_addresses = JSON.parse(JSON.stringify(addresses())); //Creates a new array with new references and data 
tmp_addresses[0].title = 'my new title'; 
addresses(tmp_addresses); 

或者,如果你可以创建地址对象,以下也能发挥作用:

var tmp_addresses = addresses(); 
tmp_addresses[0] = new randomAddressObject(); 
addresses(tmp_addresses); 

这里是一个fiddle,我证明这两种方法在单例

2

你错误地将值分配给可观察title这就是为什么UI没有反映它的变化(双向绑定断开)。

拇指规则总是使用()符号,而分配一个值来观察,(保持双向绑定完整

视图模型:

var ViewModel = function() { 
    var self = this; 
    self.addresses = ko.observableArray([{ 
     'title': ko.observable('one') 
    }, { 
     'title': ko.observable('two') 
    }]) 
    setTimeout(function() { 
    var tmp_addresses = self.addresses(); 
    tmp_addresses[0].title('blabla'); //assigning data to observable 
    self.addresses(tmp_addresses); 
    }, 2000) 
}; 
ko.applyBindings(new ViewModel()); 

工作样本here

PS:不要因为在使用=进行分配时看到viewModel中的值更改而被欺骗,因为两个绑定被破坏,UI不会反映VM的更改。

当你splice你observableArray UI需要它改变检查here

+0

)observableArray中的对象没有被声明为可观察对象,我明确地希望避免将它们声明为可观察对象。这就是为什么我的语法在更新标题时没有包含'()',请检查我在问题中所做的更新,我只想一次更新整个数组。例 – batilc