Meteor渲染回调并应用jQuery插件

one*_*lid 9 javascript jquery meteor

在将jQuery插件(如滑块或同位素)应用于从Meteor加载动态内容的DOM元素集合时寻找模式.

如果你打电话给template.rendered(这里doc)似乎是一个合乎逻辑的选择.渲染模板时应用jQuery.

根据Blaze wiki template.rendered现在只召唤一次.听起来不错.但是,没有提到将模板的内容应用于DOM 之前template.rendered调用它.

因此,推荐的方法是将内部元素放入{{#each}}子模板,然后在其rendered回调上调用jQuery .

但是:大多数jQuery插件不能以这种方式工作.需要在父DOM元素上调用它们,并且子DOM元素需要已经就位.

有些人甚至建议在父元素上使用setTimeout来近似何时呈现子元素.这对我来说听起来不够可靠.

这是一个常见的例子:

page_slider.html

<template name="page_slider">
  <div class="slider">
    <ul class="slides">
      {{#each slide}}
        {{> slider_item}}
      {{/each}}
    </ul>
  </div>
</template>
Run Code Online (Sandbox Code Playgroud)

page_slider.js

Template.page_slider.rendered = function() {
  /*
   * Can't initialise jQuery slider here as 
   * all inner DOM elements don't exist. 
   *
   */
};
Run Code Online (Sandbox Code Playgroud)

看着子模板.

slider_item.html

<template name="slider_item">
  <li class="slide"><img src="{{image}}"/></li>
</template>
Run Code Online (Sandbox Code Playgroud)

slider_item.js

Template.slider_item.rendered = function() {
  /*
   * Can't initialise jQuery slider here either 
   * as I don't know if all slide elements have been loaded 
   *
   */
};
Run Code Online (Sandbox Code Playgroud)

从上面的示例中可以看出,没有机会知道所有幻灯片何时加载到DOM中并因此调用jQuery插件.

想知道是否有一些我忽略的东西,或者是否有其他人正在使用的共同模式.

sai*_*unt 12

我用来解决这个问题的模式如下:

首先,我将用于#each在单独的辅助函数中提供块的游标外部化,因为我们稍后将重用它.

// given a slider, return associated slides from the collection
function slides(slider){
  return Slides.find({
    sliderId:slider._id
  });
}

// assumes the slider template is called with a slider as data context
Template.slider.helpers({
  slides:function(){
    return slides(this);
  }
});

<template name="pageSlider">
  {{> slider mySlider}}
</template>

Template.pageSlider.helpers({
  mySlider:function(){
    return Sliders.findOne({
      name:"mySlider"
    });
  }
});
Run Code Online (Sandbox Code Playgroud)

现在我们要做的是在#each块完成在DOM中插入我们的项之后执行代码,因此我们将"监听" #each块正在侦听的相同的反应数据源:这就是为什么我们将游标声明为单独的函数.

我们将设置一个反应计算来检测对底层Slides集合所做的更改,并且我们将执行一些代码,这些代码将等到#each块呈现DOM中的项目然后重新初始化jQuery滑块.

Template.slider.rendered=function(){
  this.autorun(_.bind(function(){
    // we assume that the data context (this.data) is the slider doc itself
    // this line of code makes our computation depend on changes done to
    // the Slides collection
    var slidesCursor=slides(this.data);
    // we wait until the #each block invalidation has finished inserting items
    // in the DOM
    Deps.afterFlush(function(){
      // here it is safe to initialize your jQuery plugin because DOM is ready
    });
  }, this));
};
Run Code Online (Sandbox Code Playgroud)

Deps.afterFlush确保我们在#each完成块隐含的DOM渲染过程之后运行我们的插件初始化代码.

setTimeouthack的工作原理是假设它将在刷新周期结束后触发,但是由于DepsAPI提供了一种专门设计用于在当前刷新周期结束后运行代码的方法,因此很难实现.

作为一个快速回顾,这是这个代码在幕后发生的事情:

  • Template.slider.rendered被调用,但#each块尚未呈现滑块项.
  • 我们设置了反应计算,我们从Slides集合中监听更新(就像#each块正在进行自己独特的计算一样).
  • 一段时间之后,Slides集合被更新,并且#each块计算以及我们的自定义计算都无效 - 因为它们依赖于SAME光标 - 因此将重新运行.现在棘手的部分是我们无法判断哪个计算首先重新运行,它可能是一个或另一个,以一种不确定的方式.这就是我们需要在Deps.afterFlush回调中运行插件初始化的原因.
  • #each块的逻辑被执行,项目将插入在DOM.
  • 刷新周期(重新运行每个无效的计算)完成,Deps.afterFlush因此执行回调. 

这种模式允许我们在将新项目添加到模型中并随后在DOM中呈现时重新初始化我们的jQuery插件(轮播,类似砖石等等)Blaze.

重新初始化过程取决于插件,大多数情况下,如果存在,您将要么调用reinit方法,要么手动销毁并重新创建插件.

Meteor手册提供了很好的解释和示例Deps以及Blaze内部,这绝对是一个推荐阅读:

http://manual.meteor.com/

  • 哇.非常感谢takin有时间解释这个问题.我还花了30分钟阅读手册中的Deps.知道Deps如何工作确实有助于我对Meteor的整体理解. (3认同)
  • 伙计,太棒了.我希望我能+10. (3认同)