如果值在外部更改,则无法检测到Knockout可观察到的更改

时间:2013-04-06 01:41:02

标签: knockout.js observable

我有一个绑定到ko.observableArray的文本框列表。

我必须确保文本框值不能为空,如果在blur()

上为空,则通过将值设置为0来使用jQuery

问题是,使用jQuery完成的值更改未被knockout注册。

如何观察模型中的值变化?

请参阅我的简化小提琴以获得重点 - http://jsfiddle.net/k45gd/1/

HTML

<input type="number" data-bind="value: age" />
<span data-bind="text: age"></span>
<button data-bind="click: setAgeExternally">I want the label to change to 0</button>

JS

var model = function() {
    this.age = ko.observable(21);

    //this code is outside of the model, this is oversimplification
    this.setAgeExternally = function(){
        $('input').val(0);
    }
};

ko.applyBindings(new model());

3 个答案:

答案 0 :(得分:14)

你需要两件事:

  1. 使用jQuery更改元素的值后,您需要让Knockout知道更新模型。您可以通过触发change事件来执行此操作:

    $('input').val(0).trigger('change');
    
  2. 对于Knockout(在3.1.0之前)响应jQuery事件,它需要知道你正在使用jQuery。要做到这一点,你必须在Knockout之前在你的文档中包含jQuery。

  3. 这是两个更新的小提琴:http://jsfiddle.net/mbest/k45gd/2/

答案 1 :(得分:5)

在您提供的示例中,您使用以下代码更新输入框:

this.setAgeExternally = function(){
    $('input').val(0);
}

考虑到输入绑定到age属性,这样做会更简单:

this.setAgeExternally = function(){
  this.age(0);
}

然而,即使这并不是真的需要,因为age属性在您的viewmodel上公开。所以外部代码可以这样做,并且真的不需要setAgeExternally方法:

model.age(0);

让我们回到原来的问题 - 您描述的问题,但不要发布代码。您提到您有一个绑定到可观察数组的输入框列表。

使用可观察数组时,您需要注意一个有趣的问题:

来自http://knockoutjs.com/documentation/observableArrays.html的文档:

  

关键点:observableArray跟踪数组中的对象,   不是那些对象的状态

     

简单地将一个对象放入一个observableArray并不能完全实现   该对象的属性本身是可观察的。当然可以   如果你愿意,可以观察这些属性,但那是一个   独立选择。 observableArray只跟踪它的对象   保持,并在添加或删除对象时通知侦听器。

根据您列出的要求,根本不需要jQuery。您可以尝试这个由三部分组成的解决方案:

  1. 使您的observableArray包含observable。所以你最终得到的结论是:

    var model = function() {
      this.ages = ko.observableArray([
        {age: ko.observable(13)},
        {age: ko.observable(18)},
        {age: ko.observable(16)},
        {age: ko.observable(13)}
      ]);
    };
    
  2. 接下来,创建一个敲除扩展器,如果空白,则自动重置为0

    ko.extenders.defaultIfBlank = function(target, defaultValue) {
      var result = ko.computed({
        read: target,  //always return the original observables value
        write: function(newValue) {
            if (newValue == "") {
              target(defaultValue);
            } else {
               target(newValue);
            }
        }
      });
    
      //initialize with current value to make sure it is not blank
      result(target());
    
      //return the new computed observable
      return result;
    };
    
  3. 将扩展器应用于数组中的observable

    var model = function() {
      this.ages = ko.observableArray([
        ko.observable(13).extend({defaultIfBlank: "0"}),
        ko.observable(18).extend({defaultIfBlank: "0"}),
        ko.observable(16).extend({defaultIfBlank: "0"}),
        ko.observable(13).extend({defaultIfBlank: "0"})
      ]);
    };
    
  4. 工作小提琴:http://jsfiddle.net/tlarson/GF3Xe/

答案 2 :(得分:0)

Michael Best建议:

这是对我有用的代码:

function insertControlsAtcaret(element, addControlText){
    if (element.selectionStart || element.selectionStart == '0') {
        //For browsers like Firefox and Webkit based
        var startPos = element.selectionStart;
        var endPos = element.selectionEnd;
        var scrollTop = element.scrollTop;
        element.value = element.value.substring(0, startPos) + addControlText + element.value.substring(endPos, element.value.length);
        $(element).trigger("change");
        element.focus();
        element.selectionStart = startPos + addControlText.length;
        element.selectionEnd = startPos + addControlText.length;
        element.scrollTop = scrollTop;
    } else {
        element.value += addControlText;
        $(element).trigger("change");
        element.focus();
    }
};

Thx MB,节省了我的时间......:)