【问题标题】:Extending an observable in a custom binding在自定义绑定中扩展可观察对象
【发布时间】:2019-04-08 10:06:54
【问题描述】:

我有一个自定义绑定处理程序,我将它绑定到我的视图模型中的一个复杂对象。

绑定处理程序正常工作,并且当任何可观察的属性更新时调用update 函数。但是,每个更新的属性都会调用update 函数,这会导致奇怪的行为,因为我依赖于整个对象可用且是最新的。

我理解为什么会发生这种情况,因为每个属性都会导致调用更新,并且我想我知道如何防止这种情况发生 - 通过使用 Knockout 的 deferred updates 功能。

但是,我无法找到如何仅为自定义绑定中的 observable 启用延迟更新。我不想在应用程序范围内启用它,因为我正在将绑定编写为库函数。

我尝试了许多不同的方法,包括:

所有这些都没有奏效。

我还没有发现任何其他自定义绑定处理程序可以远程接近此类功能,并且一直在尝试将其与其他功能拼凑在一起。

我的绑定代码本身是比较简单的,我把绑定的对象简单的拆分出来参数传给一个Code Mirror实例。

ko.bindingHandlers.editor = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var observableValue = ko.utils.unwrap(valueAccessor());
        initEditor(element, observableValue, allBindingsAccessor);
    },
    update: function(element, valueAccessor, allBindingsAccessor) {
        var observableValue = ko.unwrap(valueAccessor());

        createEditor(codeEditorDiv, observableValue);
        resize();
        updateEditor(element, observableValue, allBindingsAccessor);
    }
};

而我的 HTML 代码是:

 <div id="editor" data-bind="editor: EditorVM"></div>

我在 ViewModel 中使用Dotnetify,所以它是一个合理的复杂 C# 类,但只要说绑定正在工作和更新就足够了,但我需要它只在所有属性都更新后调用“更新” .

【问题讨论】:

    标签: javascript knockout.js ko-custom-binding


    【解决方案1】:

    很遗憾,您没有展示 initEditorcreateEditorupdateEditorobservableValue 的作用,因为这可能是您应该扩展 observables 的地方。

    绑定的initupdate 方法创建计算依赖项,这意味着从init 开始在调用堆栈中展开的任何 observable 将导致update 方法被调用。

    在一个抽象的例子中:

    const someVM = ko.observable({
      a: ko.observable(1),
      b: ko.observable(2),
      c: ko.observable(3)
    });
    
    // Some function that unwraps properties
    const logABC = function(vm) {
      console.log(
        vm.a(),
        vm.b(),
        vm.c()
      );
    }
    
    // Regular binding update:
    ko.computed(function update() {
      console.log(
        "Regular binding update:",
      )
      logABC(someVM())
    });
    
    // Change VM
    someVM(someVM());
    
    // Change a, b, and c
    someVM().a("A");
    someVM().b("B");
    someVM().c("C");
    &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"&gt;&lt;/script&gt;

    注意update 被调用:

    1. 初始化计算时
    2. 更改包含视图模型的可观察对象时
    3. 当更改视图模型的任何可观察属性时

    有几种方法可以解决这个问题,其中最简单的方法是在绑定的init 方法中创建自己的computed 并将其扩展为deferred

    const someVM = ko.observable({
      a: ko.observable(1),
      b: ko.observable(2),
      c: ko.observable(3)
    });
    
    const getABC = function(vm) {
      return [vm.a(), vm.b(), vm.c()].join(", ");
    }
    
    ko.bindingHandlers.renderABC = {
      init: function(el, va) {
        el.innerText += "Init.\n";
        
        // This ensures any inner unwrapping gets deferred
        var updateSub = ko.computed(function update() {
          el.innerText += getABC(ko.unwrap(va())) + "\n";
        }).extend({ deferred: true });
        
        ko.utils.domNodeDisposal.addDisposeCallback(el, function() {
          updateSub.dispose();
        });
      }
    }
    
    ko.applyBindings({ someVM: someVM });
    
    // Change VM
    someVM(someVM());
    
    // Change a, b, and c
    someVM().a("A");
    someVM().b("B");
    someVM().c("C");
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
    
    <pre data-bind="renderABC: someVM"></pre>

    【讨论】:

    • updateSub 是如何应用于绑定的?我所有的xEditor 方法所做的就是获取未包装的访问器并将其中的属性应用到另一个插件,因此发生的情况是update 被调用一次,用于每个 被更改的属性,而不是一次对于整个对象。
    • updateSub 计算作为延迟依赖添加到您的xEditor 方法展开的所有 可观察属性。因为我是手动创建它,所以我可以将其扩展为延迟。如果我们依赖绑定提供的update 方法,我们将获得每次更改的更新,因为没有办法推迟它。
    • 啊。这是update 函数inside 使用外部valueAccessor 计算的,这是我缺少的位。在 Knockout 文档中似乎没有任何内容。添加它可以完美运行,并且只有在整个 VM 更新后才调用 update。谢谢。
    猜你喜欢
    • 2017-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-05
    • 1970-01-01
    相关资源
    最近更新 更多