【问题标题】:Dynamically growing observableArray containing observables动态增长的 observableArray 包含 observables
【发布时间】:2016-10-10 00:23:31
【问题描述】:

我的目标是拥有一个动态增长的输入字段列表 - 每当我开始在输入字段中输入时,其下方应该会出现一个新的空字段。

我希望这样的事情能够奏效;

function Model() {
    // Build a basic observable array
    this.inputs = ko.observableArray();

    // Whenever this changes, or on initial creation of this computed
    // add a new empty field if the last one has any text in it.
    ko.computed(function () {
        var len = this.inputs().length;
        // If there are no fields (initial run) or the last element is falsey...
        if (!len || this.inputs()[len-1]()) {
            // Create a new observable and add it to the array.
            this.inputs.push(ko.observable(""));
        }
    }, this);
}

下面是一些用于绑定模型的基本 HTML;

<ul data-bind="foreach: inputs">
    <li><input data-bind="textInput: $data" /></li>
</ul>

当我在正确显示的文本框中键入内容时(显示此函数在创建时运行),计算不会被调用。

那么我必须怎么做才能让计算结果正确地重新评估?有没有更好的方法来实现在淘汰赛中实际有效的动态增长列表?

这是jsfiddle 的确切代码,用于帮助调试此问题。

【问题讨论】:

    标签: javascript html knockout.js


    【解决方案1】:

    当前实现无法按预期运行的原因有两个:

    1. 您将输入字段绑定到$data 上下文属性,根据docs,它是提供给可观察对象的字符串。绑定到 $rawData 属性以绑定到实际的 observable。
    2. Computed dependency tracking 添加在任何评估运行期间遇到的每个 observable。推送一个新的 observable 显然不会添加依赖项。将可观察数组初始化为单个可观察对象将是解决此问题的方法。通过这样做,!len 检查也可以被删除。

    function Model() {
      this.inputs = ko.observableArray([ko.observable("")]);
    
      ko.computed(function() {
        if (!!this.inputs()[this.inputs().length - 1]()) {
          this.inputs.push(ko.observable(""));
        }
      }, this);
    }
    
    ko.applyBindings(new Model());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <ul data-bind="foreach: inputs">
      <li>
        <input data-bind="textInput: $rawData" />
      </li>
    </ul>

    下面是一个替代例程。它递归地添加一个可观察的。添加 observable 时,会创建一个订阅来跟踪其更改。当它变为非空值时,订阅被释放(只需要一次)并重复该例程。

    function Model() {
      this.inputs = ko.observableArray();
    
      this.addItem = function() {
        var newItem = ko.observable("");
        this.inputs.push(newItem);
        var sub = newItem.subscribe(function(newValue) {
          if (!!newValue) {
            sub.dispose();
            this.addItem();
          }
        }, this);
      }
    
      this.addItem();
    }
    ko.applyBindings(new Model());
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <ul data-bind="foreach: inputs">
      <li>
        <input data-bind="textInput: $rawData" />
      </li>
    </ul>

    【讨论】:

    • 这似乎是一个很好的解决方案 - 但我不确定为什么在我的示例中绑定字符串而不是 observable。能详细点吗?
    • 我更新了答案,详细说明了为什么当前的实现没有按预期运行,并用更紧凑的版本更新了替代方案
    • $rawData 修复了我的实现。你的其他版本似乎也更有效 - 但我想在它们变空时折叠它们,所以我现在就离开它。谢谢大家:)
    猜你喜欢
    • 2013-05-07
    • 1970-01-01
    • 2014-10-28
    • 1970-01-01
    • 1970-01-01
    • 2017-09-26
    • 1970-01-01
    • 2023-04-03
    • 2013-05-30
    相关资源
    最近更新 更多