在自定义挖空绑定的init和update之间存储状态的首选方法是什么?

时间:2012-04-15 21:07:55

标签: knockout.js

目前我正在使用dom元素的jQuery数据存储状态。

ko.bindingHandlers.customValue = {

    init: function init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var state = { isEditing: false };        
        $(element).focus(function focus() {
            state.isEditing = true;
        }).blur(function blur() {
            state.isEditing = false;            
        }).data("customBinding", state);

    },

    update: function update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        // ignore if updating
        if (!$(element).data("customBinding").isEditing) {
            // handle update if they are not updating                                
        }
    }

};​

是否有更好的地方存储不需要dom的绑定状态? bindingContext可以用于存储绑定的每个实例的状态吗?

5 个答案:

答案 0 :(得分:47)

bindingContext是可能的,但仅限于在第一次触发绑定时将数据从init传递到updateupdate下一次发射它将不再存在。

存储此类状态的位置实际上有两种选择:

1-在元素上,如你所说。您可以使用jQuery的$.data或KO包含API来执行此操作ko.utils.domData.get(element, key)ko.utils.domData.set(element, key, value)

2-如果合适,将此类信息放入视图模型中。指示isEditing的标志在视图模型中不一定不合适。我个人喜欢把这种类型的“元数据”作为子观察者从像以下观察者那样:

var name = ko.observable("Bob");
name.isEditing = ko.observable(false);

您可以绑定namename.isEditing

这有一些优点:

  • 使视图模型保持相当干净,因为您没有引入新的顶级属性
  • 将子可观察性与其父可观察性保持联系(不需要nameIsEditing等)。
  • 当使用类似ko.toJSON之类的内容转换为JSON时,{parent} {sub} obseable将在其父项被解包时被删除。因此,您不会将不必要的值发送回服务器。
  • 在这种情况下,它还具有可用于视图模型中的其他计算或绑定到UI中的多个元素的优势。

答案 1 :(得分:8)

将数据附加到元素很好,Knockout在内部使用此方法进行控制流绑定(例如,if,with等)。

另一种方法是仅使用init函数并使用计算的observable来处理更新。我在repeat绑定中使用此方法。以下是重要部分:

ko.bindingHandlers['repeat'] = {
    'init': function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        ...
        // set up persistent data
        var lastRepeatCount = 0;
        ...
        ko.computed(function() {
            var repeatCount = ko.utils.unwrapObservable(valueAccessor());
            ...
            // Remove nodes from end if array is shorter
            for (; lastRepeatCount > repeatCount; lastRepeatCount--) {
                ...
            }
            ...
            // Add nodes to end if array is longer (also initially populates nodes)
            for (; lastRepeatCount < repeatCount; lastRepeatCount++) {
                ...
            }
        }, null, {'disposeWhenNodeIsRemoved': placeholder});
        ...
    }
};

答案 2 :(得分:1)

我经常使用这种模式:

define(['knockout'], function(ko) {
  var interInstanceVariable = null;

  function Tree(element) {
    var privateInstanceVariable = null;

    function privateInstanceMethod() {}

    this.publicInstanceMethod = function() {}
  }


  ko.bindingHandlers.cannDendrogram = {
    init: function(element, valueAccessor) {
      $(element).data('tree', new Tree(element));
    },
    update: function(element, valueAccessor) {
      var tree = $(element).data('tree');
      tree.publicMethod();
    }
  };
});

答案 3 :(得分:1)

我意识到这个问题已经过时了,但我在这里偶然发现了这种方法,因此我认为我会采用更现代的方法。

您可以直接向bindingContext。$ data添加属性。我选择了一个恼人的变量名称“___IsEditing”,以避免潜在的碰撞,但你明白了......

ko.bindingHandlers.customValue = {

init: function init(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    bindingContext.$data.___IsEditing = false;        
    $(element).focus(function focus() {
        bindingContext.$data.___IsEditing = true;
    }).blur(function blur() {
        bindingContext.$data.___IsEditing = false;            
    }).data("customBinding", state);

},

update: function update(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
    // ignore if updating
    if (bindingContext.$data.___IsEditing) {
        // handle update if they are not updating                                
    }
}

};

答案 4 :(得分:-2)

我使用函数通过定义函数内部的公共数据来为init和update创建一个公共范围。