1

我有以下组件:

<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>

现在,我想将我的 observables 的逻辑封装到组件本身中,所以我以这种方式更改了组件:

ko.components.register('fruits', {
    viewModel: function(params) {
        this.name = ko.observable(params.name);
        this.color   = ko.observable(params.color);
    },
    template: { element: 'fruits-tpl' }
});

...现在我有了可观察的 items.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]);
}

为什么在我的第二个示例中没有更新主视图模型中的基础数据,尽管 items.choice 仍然是可观察的?我确定我错过了一些概念,也许我的可观察数组中的每个项目也应该是可观察的,但我不明白是否有办法让第二个例子工作。

第一个例子:http: //jsfiddle.net/5739ht0q/2/ 第二个例子:http: //jsfiddle.net/079tx0nn/

4

2 回答 2

3

有几种方法可以将数据更新回主视图模型,如下所示。

但首先,让我们稍微改进一下主视图模型。

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>

这里的要点是:

  • 将 observable 传递给组件,而不仅仅是数据,以及
  • 打破组件内部参数的依赖链

完整的 Fiddle 嵌套组件:http: //jsfiddle.net/jo37q7uL/

于 2016-03-12T12:50:38.380 回答
0

Observables 是淘汰赛的核心,它允许在视图和 ViewModel 之间进行 2 路绑定。要更新组件的 JSON 字段,需要更改“VM”有权访问的可观察对象。

为了避免定义 Fruit 和 Dessert 函数,您可以使用Knockout 映射将数据数组转换为ko.observableArray. 这会将数组中每个对象的字段设置为可观察对象,以便将它们传递给组件绑定。

于 2016-02-28T10:59:16.160 回答