动态更新后自动在敲除绑定处理程序中应用砌体

auto apply masonry in knockout binding handler after dynamic update

我正在使用 knockoutjs with masonry 并创建了自定义挖空绑定处理程序以将砌体应用到 html 元素。

我想应用砌体的容器使用 knockout 的 foreach 绑定动态注入了内容。

我遇到的问题是在动态更新砌体容器后让砌体自行应用。

在代码片段示例中,如果您单击 masonryize 按钮,它将 destroy masonry 容器并重新应用 mansonry,我如何获得这种行为进入我的绑定处理程序?

ko.bindingHandlers.masonry = {
  update: function(element, valueAccessor) {
    var options = valueAccessor();
    $(element).masonry(options);
  }

}

var vm = {
  term: ko.observable(),
  page: ko.observable(1),
  per_page: ko.observable(3),
  items: ko.observableArray(),
  masonryize: function() {
    $('.grid').masonry('destroy');
    $('.grid').masonry({
      itemSelector: '.item',
      columnWidth: 200
    });
  },
  getStuff: function() {
    $.ajax({
      url: 'https://api.github.com/search/repositories',
      method: 'GET',
      data: {
        q: this.term,
        page: this.page(),
        per_page: this.per_page()
      }
    }).then(function(r) {
      this.items(r.items)
    }.bind(this))
  }
}

ko.applyBindings(vm);
.grid {
  width: 400px;
}
.item {
  margin-bottom: 2em;
  border: 1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://masonry.desandro.com/masonry.pkgd.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>


<div>
  <label>term:</label>
  <input type="text" data-bind="value: term" />
</div>
<div>
  <label>page:</label>
  <input type="text" data-bind="value: page" />
</div>
<div>
  <label>no of items:</label>
  <input type="text" data-bind="value: per_page" />
</div>

<div>
  <button data-bind="click: getStuff">get stuff</button>
  <button data-bind="click: masonryize">masonryize</button>
</div>

<div class="grid" data-bind="masonry: {itemSelector: '.item', columnWidth: 200}, foreach: items">
  <div class="item">
    <p data-bind="text:name"></p>
    <p data-bind="text:language"></p>
  </div>
</div>

这取决于你在masonsy容器更新后所说的意思。您是说调整大小还是加载新项目?如果您知道容器何时需要更新,您可以这样做:

ko.bindingHandlers.masonry = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        ko.computed(function () {
             var options = ko.unwrap(valueAccessor());

             ko.unwrap(allBindingsAccessor().masonryUpdater);

             $(element).masonry('destroy');
             $(element).masonry(options);
        }, null, disposeWhenNodeIsRemoved: element);
    }
}

// ...

vm.masonryUpdater = ko.observable();

// When you need to update just run

vm.masonryUpdater.valueHasMutated();

并像这样应用您的绑定:

<div class="grid" data-bind="masonry: {itemSelector: '.item', columnWidth: 200}, masonryUpdater: masonryUpdater, foreach: items">
  <div class="item">
    <p data-bind="text:name"></p>
    <p data-bind="text:language"></p>
  </div>
</div>

所以这里发生了什么:

  1. 我们通过读取它来创建对可观察对象 allBindingsAccessor().masonryUpdater 的订阅。这样,每当它更新我们创建的计算时,都会重新评估并重新应用砌体。
  2. 为了告诉 knockout 我们的 observable 已经改变,我们调用 vm.masonryUpdater.valueHasMutated();
  3. 我们的网格元素被移除后计算的将被安全地处理

这种看起来像巫术,而且有些人可能会说我们在利用副作用。好吧,也许只有一点点。从好的方面来说,我们正在让事情变得非常简单。所有需要做一次的事情都在计算外执行,所有需要依赖或更新的事情都在计算内执行。也更容易调试和保存数据以备后用。

这里描述的比较详细http://www.knockmeout.net/2012/06/knockoutjs-performance-gotcha-3-all-bindings.html