knockout:当模板内容发生变化时的回调函数

时间:2017-05-26 23:10:58

标签: javascript html templates knockout.js callback

我希望在模板内容发生变化时抓住事件;换句话说,每次重新渲染模板后。

示例:

<script type="text/html" id="myTemplate">
<span data-bind="text: myBoolean"></span>
</script>
<!-- ko template: { name: 'myTemplate', afterRender: myafterRenderCallback } -->
<!-- /ko-->

在javascript中,我只想:

self.myBoolean = ko.observable(true);

这个可观察的变化,比方说,还有一些其他按钮,点击它会改变myBoolean的值。

因此,每次myBoolean更改时,模板的内容都会更新。完成模板的重新渲染时,我需要回调函数,这意味着它将值从true更改为false,或者从false更改为true。

我尝试了两种解决方案,但没有一种方法有效:

  1. afterRender: myafterRenderCallback仅在第一次呈现模板时开始调用一次。每次myBoolean更改后都不会调用它。

  2. 对myBoolean:

    使用类似侦听器

    self.myBoolean.subscribe(function(){

    // TODO

    });

  3. 第二种方法的问题在于,即使在模板在视图中更新myBoolean之前,订阅函数也被调用得太快,这导致了问题,这是一个很长的故事要告诉我(如果你真的,我可以想知道:例如,在一个简单的例子中,我需要访问DOM,模板比上面的模板更复杂。

    我尝试了另一种使用setTimeout的方法,所以在myBoolean更改后等待2秒左右,但这更像是一个肮脏的解决方案。

    任何建议都表示赞赏。

    =================================

    有关所请求的模板内容的信息:

    所以我在这里给你更多信息:我有一个名为ToolBar的淘汰组件。这是一个模板;它只是一个带有一些CSS的容器。没有那么多。它包含一组按钮。有些按钮可以是可见的,有时是不可见的(让我们称之为按钮的状态)。现在,每当按钮改变其状态时,我们希望残疾用户能够仅使用键盘(它是WAI-ARIA)来浏览按钮,通常按Tab键或箭头键。

    问题是,只要按钮动态改变其状态,它就会破坏键盘的可访问性。这种功能破坏的原因是,通过键盘控制导航的(无论哪种)技术都不知道按钮的新更改状态。示例:按Tab或箭头键将跳过新显示的按钮,或卡在新看不见的按钮上。或者解决这个问题,当DOM完成重新渲染时,我需要简单地刷新工具栏(有一个刷新功能),以便WAI-ARIA知道所有按钮的最终状态,从而实现导航键盘。

    因此,当模板的整个重新渲染完成时,即当所有按钮都已完成改变其状态时,需要调用刷新功能。因此,我需要该事件或对该事件的回调。

    依赖于observables(上面提到的方法2)会导致一个问题:在更新DOM之前调用observable的subscribe函数,因此调用刷新函数也会更新DOM,因此太早,并且键盘只是对按钮的旧状态进行操作,这导致导航中断。

    请注意,此处没有异步过程。按钮的状态只是由于选择了哪一行而改变,或者在一个简化的例子中,让我们想象其他地方还有其他按钮,点击它可以改变工具栏中按钮的状态。

2 个答案:

答案 0 :(得分:1)

正如我在上面的评论中所建议的那样。我相信这句话:

  

问题是,每当按钮动态改变其状态时,它就会出现   用键盘打破了可访问性。

包含答案。

它表明了一个&#34;自定义绑定&#34;这就是我们需要的。此绑定将使用update函数来侦听observable的状态更改(或者,如果正在侦听多个observable,则会计算)以重新绑定可访问性键盘绑定。

代码如下:

ko.bindingHandlers.accessibilityBindings = {
    update: function(element, valueAccessor) {
        //keyboard binding code goes here
    }
};

答案 1 :(得分:0)

我尝试收集所有在一个(可观察)数组中启用/禁用/更改模板的observable。然后,您可以在延迟ko.computed中评估它们以触发某些更新/回调函数。

E.g:

this.buttonStates = [ this.button1Enabled, this.button2Enabled ];

ko.computed(function someButtonUpdated() {
  this.buttonStates.forEach(function(state) { state(); });
  myCallbackFunction();
}, this).extend({ deferred: true });

如果您不想跟踪视图中的状态,可以使用在buttonStates中注册(推送到init)的自定义绑定,并取消注册(从buttonStates)关于节点删除。

<button data-bind="enable: button1Enabled, 
                   register: { 
                     obs: button1Enabled, 
                     arr: buttonStates 
                   }">button 1</button>

正如您所看到的,这种方法确实为您的视图和视图模型添加了一些额外的代码和复杂性......所以我不确定它是否适合您。它会让你免于实现MutationObserver,这可能会更复杂......

这是一个有效的例子:

&#13;
&#13;
ko.bindingHandlers.register = {
  init: function(element, valueAccessor) {
    var settings = valueAccessor();
    
    settings.arr.push(settings.obs);
    
    ko.utils.domNodeDisposal.addDisposeCallback(
      element, 
      function() {
        settings.arr.remove(settings.obs);
      }
    );
  }
}

var App = function() {
  this.deps = ko.observableArray([]);
  ko.computed(function() {
    // Create dependencies
    this.deps().forEach(function(dep) {
      dep();
    });

    renderSub();
  }, this).extend({
    "deferred": true
  });

  // Some stuff to mimic an updating vm + ui state
  this.hideAll = ko.observable(false);
  this.count = ko.observable(0);
  this.inc = function() {
    this.count(this.count() + 1);
  }.bind(this);

  // Button states
  this.enable1 = ko.pureComputed(function() {
    return this.count() % 2 === 0;
  }, this);

  this.enable2 = ko.pureComputed(function() {
    return this.count() % 3 === 0;
  }, this);

  this.enable3 = ko.pureComputed(function() {
    return !this.enable1() && !this.enable2();
  }, this);
}


var renderSub = (function() {
  var i = 0;
  return function() {
    // Count renders and show ui is updated
    console.log(
      "Render", 
      ++i,
      Array
        .from(document.querySelector(".tmpl").children)
        .map(c => c.disabled ? 'disabled': 'enabled')
        .join(" ")
    );
  };
}());

ko.applyBindings(new App());
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div class="tmpl" data-bind="ifnot: hideAll">
  <button data-bind="enable: enable1, register: { obs: enable1, arr: deps }">button 1</button>
  <button data-bind="enable: enable2, register: { obs: enable2, arr: deps }">button 2</button>
  <button data-bind="enable: enable3, register: { obs: enable3, arr: deps }">button 3</button>
</div>

<button data-bind="click: inc, text: count() + ' + 1'"></button>
<button data-bind="click: hideAll.bind(null, !hideAll())">toggle</button>
&#13;
&#13;
&#13;