Meteor渲染回调并应用jQuery插件 [英] Meteor rendered callback and applying jQuery Plugins

查看:84
本文介绍了Meteor渲染回调并应用jQuery插件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Looking for a pattern when applying a jQuery plugin -- like a slider or isotope -- to a collection of DOM elements that are loaded with dynamic content from Meteor.

如果您调用 template.rendered doc here )似乎合乎逻辑的选择。渲染模板时应用jQuery。

If you call the template.rendered (doc here) seems like a logical choice. When the template is rendered apply the jQuery.

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

According to the Blaze wiki template.rendered is now only called once. Sounds good. However it doesn't mentioned that template.rendered is called before the contents of the template are applied to the DOM.

所以推荐的方法是将内部元素放入{{#each}}子模板,然后在他们的渲染时调用jQuery 回调。

So the recommended method is to put the inner elements into an {{#each}} sub-template and to then call jQuery on their rendered callback.

但是:大多数jQuery插件都不能正常工作。需要在父DOM元素上调用它们,并且子DOM元素需要已经就位。

However: Most jQuery plugins don't work this way. They need to be called on the parent DOM element, and the child DOM elements need to already be in place.

有些甚至推荐父元素上的setTimeout,用于估算子元素的呈现时间 。这对我来说听起来不够可靠。

Some goes as far as recommending a setTimeout on the parent element to approximate when the child elements will be rendered. This doesn't sound reliable enough for me.

这是一个常见的例子:

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插件。

As you can see from the sample above, there's no opportunity to know when all slides have been loaded into the DOM and to therefore call a jQuery plugin.

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

Wanting to know if there is something I've overlooked, or if there is currently a common pattern others are using.

推荐答案

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

The pattern I'm using for addressing this purpose is the following :

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

First, I externalize the cursor used to feed the #each block in a separate helper function because we are going to reuse it later.

// 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"
    });
  }
});

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

Now what we'd like to do is execute code after the #each block has finished inserting our items in the DOM, so we are going to "listen" to the same reactive data source that the #each block is listening to : this is why we declared the cursor as a separate function.

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

We'll setup a reactive computation to detect changes being made to the underlying Slides collection, and we'll execute some code that is going to wait until the #each block renders items in the DOM then reinitialize the jQuery slider.

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渲染过程完成后运行我们的插件初始化代码。

Deps.afterFlush is assuring us that we run our plugin initialization code AFTER the DOM rendering process implied by the #each block is done.

setTimeout hack的工作方式是假设它会在刷新周期结束后触发,但是在 Deps API提供的情况下很难实现一个专门设计用于在当前刷新周期结束后运行代码的方法。

The setTimeout hack works by assuming that it will trigger after the flush cycle has finished, but is is ugly insofar as the Deps API provides a method specifically designed to run code after the current flush cycle is over.

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

As a quick recap, this is what is happening with this code under the hood :


  • 调用Template.slider.rendered ,但 #each block尚未呈现您的滑块项目。

  • 我们设置了反应计算,我们从幻灯片集合(就像 #each 块正在进行自己独特的计算)。

  • 一段时间之后,幻灯片集合已更新且两者都有#each块计算以及我们的自定义计算无效 - 因为它们依赖于SAME游标 - 因此将重新运行。现在棘手的部分是我们无法判断哪个计算首先重新运行,它可能是一个或另一个,以一种不确定的方式。这就是为什么我们需要在 Deps.afterFlush 回调中运行插件初始化。

  • #each 执行块逻辑并将项插入DOM中。

  • 刷新周期(重新运行每个无效的计算)完成,我们的 Deps .afterFlush 因此执行回调。 

  • Template.slider.rendered is called but the #each block has not yet rendered your slider items.
  • Our reactive computation is setup and we listen to updates from the Slides collection (just like the #each block is doing in its own distinct computation).
  • Some time later, the Slides collection is updated and both the #each block computation as well as our custom computation are invalidated - because they depend on the SAME cursor - and thus will rerun. Now the tricky part is that we can't tell which computation is going to rerun first, it could be one or the other, in an nondeterministic way. This is why we need to run the plugin initialization in a Deps.afterFlush callback.
  • The #each block logic is executed and items get inserted in the DOM.
  • The flush cycle (rerunning every invalidated computations) is done and our Deps.afterFlush callback is thus executed. 

此模式允许我们重新初始化我们的jQuery插件(轮播) ,类似砖石的东西等......每当新项目被添加到模型中并随后通过 Blaze 在DOM中呈现。

This pattern allows us to reinitialize our jQuery plugins (carousels, masonry-like stuff, etc...) whenever new items are being added to the model and subsequently rendered in the DOM by Blaze.

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

The reinitialization process is plugin dependent, most of the time you will have either to call a reinit method if present or manually destroy and recreate the plugin.

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

The Meteor manual provides great explanations and examples of Deps and Blaze internals, this is definitely a recommended reading :

http://manual.meteor.com/

这篇关于Meteor渲染回调并应用jQuery插件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆