如何构建一个调用链,在 marrionete.js 中使用 itemView 中的异步函数延迟渲染 itemView

how to build a chain of calls render itemViews in marrionete.js deferred with async function inside itemView

我的结构很简单

ProductsList.TileListView = Marionette.CollectionView.extend({
    tagName: 'section',
    className: 'b-list-viewtile',
    template: '/static/js/app/list/modules/productslist/templates/tileList',
    childView: ProductsList.TileItemView

});



ProductsList.TileItemView = Marionette.ItemView.extend({
    tagName: "article",
    className: "b-list-viewtile__item",
    template:  '/static/js/app/list/modules/productslist/templates/tileItem',
    _getImage: function(){

        var _this = this;

        var img =  new Image();
        img.src = this.model.get("media");
        img.onload = function(){
            msnry.append( _this.$el )
                .masonry( 'appended', _this.$el ).masonry('layout');
        };
    }
}); 

但我使用 Masonry 插件在网格上对齐项目。

问题是对于每个项目,我需要知道变量的值,例如图像的高度。

这意味着构建每个 itemView 我只能在 DOM 中附加上一个项目。

保持集合中的顺序也很重要 - 集合中的第一个应该首先呈现等。

花了很多时间研究文档,一直没有找到优雅的解决方案

谁能告诉我如何构建 "chain"?每个项目必须等待渲染之前,但最终结果顺序必须保持在集合中,而不是图像加载优先级。

我认为 deffered+marrionette.callback 并覆盖原生 attachHtml 或 buildChildView

非常感谢:)

我们将使用 Marionette 的内置 Marionette.Babysitter 和 ES6 Promises 来解决您的问题。

如果您仔细阅读 docs(或浏览源代码),您将了解到 Marionette 按集合顺序呈现 child 视图。

By default the CollectionView will maintain the order of its collection in the DOM.

此外,所有 CollectionView 也将持有对其所有 children 视图的引用,按照它们呈现的顺序,这也是它们的集合排序顺序。

您想要的是确保所有 children 视图都已呈现,然后将 child 视图 el 添加到您的 Masonry 项目列表中,顺序.

首先,设置您的 Promise

我们将稍微修改您的 children 视图的 ._getImage 方法,并允许它 return 一个 Promise:

_getImage: function(){
    var _this = this;
    return new Promise(function (resolve, reject) {
      var img =  new Image();
      img.src = this.model.get("media");
      img.onload = function(){
          resolve();
      };
    });
}

当加载这个任意 child 视图的图像时,由 getImage 编辑的承诺 return 将被解析。这对我们有什么帮助?继续阅读。

其次,等待所有图像加载完毕

在您的 CollectionView 中,您将添加此 属性、onRenderCollection,当您的所有 children 视图完成呈现时,它由 Marionette 触发。在这里,使用 Babysitter,我们将启动您的 children 视图的 _getImage 方法,并设置您正在寻找的 Masonry 承诺链。

onRenderCollection: function () {
  var promises = [];
  this.children.each(function (childview) {
    promises.push(childview._getImage()); // return the promise
  })
  // When all your children view's images have loaded we begin to fill Masonry
  Promise.all(promises).then(_.bind(function () {
    this.children.each(function (childview) {
      childview.addToMasonry();
    }
  }, this))
}

第三,加入Masonry!

当然,在您的 children 视图中,我们还没有一个名为 addToMasonry 的 属性,但我想您会使用一个看起来很像您的视图有你的 img.onload 回调。像这样

addToMasonry: function () {
  masonry.append( this.$el )
    .masonry( 'appended', this.$el ).masonry('layout');
}

缺点

使用此方法的一个警告是,您首先必须等到所有 children 视图都已呈现。如果列表很长(超过 1000 条),这可能是性能问题,解决方法可能并不简单。