[英]Knockout component with observable object doesn't update data
我有以下組件:
<template id="fruits-tpl">
<p>Name: <input data-bind="value: name" /></p>
<p>Type: <input data-bind="value: color" /></p>
</template>
ko.components.register('fruits', {
viewModel: function(params) {
this.name = params.name;
this.color = params.color;
},
template: { element: 'fruits-tpl' }
});
我將這個組件與下面的視圖模型一起使用,其中我的可觀察列表中的項屬於不同的類型並具有不同的屬性:
function Fruit(data) {
this.name = ko.observable(data.name);
this.color = ko.observable(data.color);
}
function Dessert(data) {
this.name = ko.observable(data.name);
this.packaging = ko.observable(data.packaging);
}
function Vm(){
var data = [{name:"Apples",color:"Yellow"},{name:"Cookies",packaging:"Box"}];
this.items = ko.observableArray([new Fruit(data[0]),new Dessert(data[1])]);
this.items.choice = ko.observable(this.items()[0]);
}
每當我更改輸入框中的文本時,此組件的效果都很好,並且基礎數據也會更新:
<div data-bind="component: {name: 'fruits', params: items.choice}"></div>
現在,我想將可觀察對象的邏輯封裝到組件本身中,因此我以這種方式更改了組件:
ko.components.register('fruits', {
viewModel: function(params) {
this.name = ko.observable(params.name);
this.color = ko.observable(params.color);
},
template: { element: 'fruits-tpl' }
});
...現在我只在內部有可觀察的item.choice數據:
function Vm(){
var data = [{name:"Apples",color:"Yellow"},{name:"Cookies",packaging:"Box"}];
this.items = ko.observableArray(data);
this.items.choice = ko.observable(this.items()[0]);
}
為什么我的第二個示例中的主視圖模型中的基礎數據沒有更新,盡管item.choice仍然可以觀察到? 我確定我缺少一些概念,也許我的可觀察數組中的每個項目也應該是可觀察的,但是我不知道是否有辦法使第二個示例工作。
第一個示例: http : //jsfiddle.net/5739ht0q/2/第二個示例: http : //jsfiddle.net/079tx0nn/
有幾種方法可以將數據更新回主視圖模型,如下所示。
但是首先,讓我們稍微完善一下主視圖模型。
function Vm(data) {
var self = this;
self.items = ko.observableArray(ko.utils.arrayMap(data, function(item) {
return ko.observable(item);
}));
self.items.choice = ko.observable(0);
self.items.choice.data = ko.computed(function() {
return self.items()[self.items.choice()];
});
}
第一種快速且骯臟的方法是將在主視圖模型內部定義的函數傳遞給組件:
<div data-bind="component: {
name: 'fruits',
params: {index: items.choice(), data: items.choice.data(), update: items.update}
}"></div>
在組件內部,我們可以調用函數來保存更改:
self.data = ko.computed(function(){
params.update(params.index, ko.toJS(self));
});
現在,主視圖模型中的更新功能顯而易見,無需為此花費更多的時間。
第二種方法是使用可訂閱的對象沿視圖模型建立通信:
ko.intramodels = new ko.subscribable();
從組件,發送通知:
self.data = ko.computed(function(){
ko.intramodels.notifySubscribers(ko.toJS(self), "updateFruits");
});
主視圖模型內部的訂閱將接收並保存更改,或多或少是這樣的:
ko.intramodels.subscribe(function(newValue) {
self.items.replace(self.items()[self.items().index], newValue);
}, self, "updateFruits");
當然,這可以像上面一樣手工完成,但是偉大的Ryan Niemeyer的信箱庫將是此處的最佳選擇: https : //github.com/rniemeyer/knockout-postbox 。
我測試了這兩種解決方案,但是不幸的是,當我激活-淘汰賽3.4中的新功能-延遲更新選項時,我遇到了一些麻煩: ko.options.deferUpdates = true;
因為我收到“超出最大調用堆棧大小”錯誤。
因為我不想放棄並錯過淘汰賽3.4的這一新的出色性能增強功能,並且由於此錯誤也或多或少地意味着,
循環依賴關系是設計錯誤,請重新考慮實現的某些部分,
我更改了視圖模型,以使依賴項跟蹤鏈僅在一個方向上起作用,而對整個組件數據僅使用一個可觀察的模型:
ko.components.register('fruits', {
viewModel: function(params) {
var self = this;
self.data = params.peek();
self.item = {};
self.item.name = ko.observable(self.data.name);
self.item.color = ko.observable(self.data.color);
self.update = ko.computed(function() {
params(ko.toJS(self.item));
});
},
template: {
element: 'fruits-tpl'
}
});
到目前為止,對於嵌套組件而言,這更明顯了,這些組件內部具有所有數據處理和可觀察的創建,並且主視圖模型不必了解子級內部的內容以及原因-只需傳遞和接收即可。返回一個可觀察對象:
<div data-bind="component:{name:'fruits',params:items.choice.data}"></div>
<template id="containers-tpl">
<div data-bind="foreach: containers">
<p><input data-bind="textInput: quantity"><span data-bind="text: name"></span></p>
</div>
</template>
<template id="fruits-tpl">
<p>Name:<input data-bind="textInput: item.name"></p>
<p>Color:<input data-bind="textInput: item.color"></p>
<div data-bind="component:{name:'containers',params:item.containers}"</div>
</template>
這里的關鍵點是:
帶有嵌套組件的完整提琴: http : //jsfiddle.net/jo37q7uL/
Observables是敲除的核心,並允許view和ViewModel之間的2種方式綁定。 為了使JSON字段更新,組件需要更改“ VM”可以訪問的可觀察對象。
為了避免定義Fruit和Dessert函數,可以使用Knockout映射將數據數組轉換為ko.observableArray
。 這會將數組中每個對象的字段設置為可觀察的,以便可以將它們傳遞給組件綁定。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.