Meteor呈现回调并应用jQuery插件

Meteor rendered callback and applying jQuery Plugins

本文关键字:应用 jQuery 插件 回调 Meteor      更新时间:2023-09-26

将jQuery插件(如滑块或同位素)应用于加载了Meteor动态内容的DOM元素集合时,请查找模式。

如果您调用template.rendered(此处为doc)似乎是一个合乎逻辑的选择。当模板被呈现时,应用jQuery。

根据Blaze维基的说法,template.rendered现在只被调用过一次。听起来不错。然而,它没有提到template.rendered在模板的内容应用于DOM之前被调用

因此,推荐的方法是将内部元素放入{{#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>

page_slider.js

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

查看子模板

slider_item.html

<template name="slider_item">
  <li class="slide"><img src="{{image}}"/></li>
</template>

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 
   *
   */
};

正如您从上面的示例中看到的那样,没有机会知道何时所有幻灯片都已加载到DOM中,因此无法调用jQuery插件。

想知道是否有我忽略的东西,或者目前是否有其他人正在使用的常见模式。

我用于解决此问题的模式如下:

首先,我将用于馈送#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"
    });
  }
});

现在,我们想做的是在#每个块在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));
};

Deps.afterFlush向我们保证,我们在#each块所暗示的DOM渲染过程完成后运行插件初始化代码。

setTimeout破解的工作原理是假设它将在刷新周期结束后触发,但由于Deps API提供了一种专门设计用于在当前刷新周期结束之后运行代码的方法,因此它是丑陋的。

简单回顾一下,这就是下面这个代码所发生的事情:

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

这种模式允许我们在向模型中添加新项并随后由Blaze在DOM中呈现时,重新初始化jQuery插件(旋转木马、类似砖石的东西等)。

重新初始化过程依赖于插件,大多数时候,您必须调用reinit方法(如果存在),或者手动销毁并重新创建插件。

Meteor手册提供了DepsBlaze内部构件的详细说明和示例,这绝对是一本推荐读物:

http://manual.meteor.com/

您也可以使用以下答案中指定的Meteor.defer()

https://stackoverflow.com/a/31414707/3051080

Template.templateNotInDomYet.onRendered(function(){
  Meteor.defer(function(){
    $(//code);
  });
});