将observableArray订阅到计算中

时间:2013-10-02 16:17:17

标签: javascript knockout.js

我有一个在数据库的表行上构造的对象。它具有在该条目中找到的所有属性以及几个ko.computed,它们是输入字段和显示内容之间的中间层。我需要它们才能为某些字段值翻译外键。

问题如下:其中一个属性是字符串的ID。我用计算机检索该ID。现在在计算中将有一个如下所示的值:'option1 | option2 | option3 | option4'

我希望用户能够更改选项,添加新选项或交换它们,但我还需要监视用户正在做什么(至少当他添加,删除或移动一个属性时)。因此,我创建了一个可观察的数组,我将以一种允许我监视用户操作的方式绑定它。然后数组将订阅计算所以它也会更新数据库中的值。

部分代码:

function Control(field) {
    var self = this;
    self.entry = field;  // database entry
    self.choices = ko.observableArray();
    self.ctrlType = ko.computed({
        read: function () {
           ...
        },
        write: function (value) {
            if (value) {
                ...
            }
        },
        owner: self
    });
    self.resolvedPropriety = ko.computed({
        read: function () {
            if (self.ctrlType()) {
                var options = str.split('|');
                self.choices(createObservablesFromArrayElements(options));
        return str;
            }
            else {
                return '';
            }
        },
        write: function (value) {
            if (value === '') {
                //delete entry      
            }
            else {
                    //modify entry
            }
        },
        deferEvaluation: true,
        owner: self
    });
    self.choices.subscribe(function (newValue) {
        if (newValue.length !== 0) {
            var newStr = '';
            $.each(newValue, function (id, el) {
                newStr += el.name() + '|';
            });
            newStr = newStr.substring(0, newStr.lastIndexOf("|"));
            if (self.resolvedPropriety.peek() !== newStr) {
                self.resolvedPropriety(newStr);
            }
        }
    });
self.addChoice = function () {
    //user added an option
        self.choices.push({ name: ko.observable('new choice') });
    };
    self.removeChoice = function (data) {
    //user removed an option
        if (data) {
            self.choices.remove(data);
        }
    };
    ...
}

这种组合有效,但不是我想要的。这是一种循环行为,它会触发太多次。这会给用户的操作带来一些重载,因为对数据库有很多请求。

我错过了什么?或者有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

来自knockout computed observable documentation

  

...在依赖链中包含循环是没有意义的。

我从帖子中解释的基本功能:

  • 根据字段选择,显示属性/选项列表
  • 能够编辑所述属性/选项
  • 能够添加属性/选项
  • 能够删除属性/选项
  • 能够对属性/选项进行排序(在那里,你必须点击文本字段的结尾/边缘)
  • 能够保存更改

因此,我提供了一个功能的骨架示例,除了最后一个,您描述了@ JSfiddle将更改应用到数据库的能力可以通过多种方式解决;除非您愿意牺牲连接开销,否则所有这些都不应包括对任何更改数据的计算或订阅。通过将数据(我假设可以在一个服务调用中收集)格式化为一个漂亮的嵌套可观察视图模型并传递适当的可观察量,您可以排除对任何ko.computed的需求。

<强> JS

 var viewModel = {
 availableFields : ko.observableArray([
    ko.observable({fieldId: 'Field1', 
                   properties: ko.observableArray([{propertyName: "Property 1.1"}])}),
    ko.observable({fieldId: 'Field2', 
                   properties: ko.observableArray([{propertyName:"Property 2.1"}, 
                                                   {propertyName:"Property 2.2"}])})]),

 selectedField: ko.observable(),

 addProperty: function() {
    var propertyCount = this.selectedField().properties().length;

    this.selectedField().properties.push({propertyName: "Property " + propertyCount})
 },
};

ko.applyBindings(viewModel);
$("#field-properties-list").sortable({
   update: function (event, ui) {
   //jquery sort doesnt affect underlying array so we have to do it manually          
   var children = ui.item.parent().children();
   var propertiesOrderChanges = [];
   for (var i = 0; i < children.length; ++i) {
     var child = children[i];
     var item = ko.dataFor(child);
     propertiesOrderChanges.push(item)
   }
   viewModel.selectedField().properties(propertiesOrderChanges);
 }
});

<强> HTML

<span>Select a field</span>

<select data-bind='foreach: availableFields, value: selectedField'>
 <option data-bind='text: $data.fieldId, value: $data'></option>
</select>

<div style="padding: 10px">
 <label data-bind='text: "Properties for " + selectedField().fieldId'></label>
<button data-bind='click: $root.addProperty'>Add</button>
<ul id='field-properties-list' data-bind='foreach: selectedField().properties'>
    <li style = "list-style: none;">
        <button data-bind="click: function() {  $root.selectedField().properties.remove($data) }">Delete</button>
        <input data-bind="value: $data.propertyName"></input>
    </li>
</ul>
</div>