Meteor:模板用数据渲染后调用函数

Posted

技术标签:

【中文标题】Meteor:模板用数据渲染后调用函数【英文标题】:Meteor: Call function after template is rendered with data 【发布时间】:2014-10-10 19:42:45 【问题描述】:

我想在轮播中显示一些帖子。对于轮播,我使用 OwlCarousel 。

    <div class="owl-carousel" id="featured-carousel">
        #each featuredPosts
        <div>
            <h2>
                postTitle
            </h2>
        </div>
        /each
    </div>

我这样称呼我的轮播:

Template.featuredCarousel.rendered = function()
$('#featured-carousel').owlCarousel(
    loop:true,
    autoplay:true,
    autoplayTimeout:3000,
    items:1,
    smartSpeed:1080,
    padding:80
);
this.rendered = true;
;

我得到的结果是,猫头鹰基本上认为我只有一个项目可以在轮播中显示,这是多个 div。显然发生的是 Template.featuredCarousel.rendered 中的函数在模板的#each-part 完成之前或数据到达之前被调用。

如何使实例化轮播的函数仅在模板完全呈现后才被调用,包括所有数据?

非常感谢您的帮助。

P.S.:我使用 Iron-router 进行路由,如下所示:

Router.map(function()
this.route('home', 
    path: '/',
    waitOn: function()
        return Meteor.subscribe('featured');
    ,
    data: function()
        return featuredPosts: Featured.find();
    
);
);

P.P.S.:我也尝试过使用加载模板,但这也无济于事。

【问题讨论】:

【参考方案1】:

您已经正确地指出了您的问题:

显然发生的是 Template.featuredCarousel.rendered 中的函数在模板的#each-part 完成之前或数据到达之前被调用。

模板的rendered 回调仅在您的模板实例首次插入 DOM 时调用一次,因此如果您的数据尚未准备好(从服务器获取)但 #each 块不会生成任何 html 元素当你实例化你的轮播时,它会显示为空。

您可以做的是确保您的数据在您的rendered 回调触发之前准备就绪。 显然您尝试设置此解决方案没有运气,您确定您像这样添加了默认加载钩子吗?

Router.onBeforeAction("loading");

更好的解决方案是在呈现的回调中侦听数据库更改,并在首次获取项目时相应地重新初始化轮播,然后动态添加和/或删除。

HTML

<template name="carousel">
  <div class="owl-carousel">
    #each featuredPosts
      ! item declaration
    /each
  </div>
</template>

JS

function featuredPosts()
  return Featured.find();


Template.carousel.helpers(
  // feed the #each with our cursor
  featuredPosts:featuredPosts
);

Template.carousel.rendered=function()
  var options=...;
  // first initialization
  this.$(".owl-carousel").owlCarousel(options);
  this.autorun(_.bind(function()
    // this is how we "listen" for databases change : we setup a reactive computation
    // and inside we reference a cursor (query) from the database and register
    // dependencies on it by calling cursor.forEach, so whenever the documents found
    // by the cursor are modified on the server, the computation is rerun with
    // updated content, note that we use the SAME CURSOR that we fed our #each with
    var posts=featuredPosts();
    // forEach registers dependencies on the cursor content exactly like #each does
    posts.forEach(function(post)...);
    // finally we need to reinit the carousel so it take into account our newly added
    // HTML elements as carousel items, but we can only do that AFTER the #each block
    // has finished inserting in the DOM, this is why we have to use Deps.afterFlush
    // the #each block itself setup another computation and Deps.afterFlush makes sure
    // this computation is done before executing our callback
    Tracker.afterFlush(_.bind(function()
      this.$(".owl-carousel").data("owlCarousel").reinit(options);
    ,this));
  ,this));
;

我不熟悉 owl-carousel,所以我不确定 reinit 是否会正常运行(我很快检查了文档,但似乎还可以)。

对于没有 reinit 方法的类似 JS 插件(例如引导轮播),您可以先检查插件是否已实例化,然后将其销毁并重新创建,如下所示:

var carousel=this.$(".carousel").data("bs.carousel");
if(carousel)
  // bootstrap carousel has no destroy either so we simulate it by pausing it
  // and resetting the data attribute to null (it's a bit messy)
  this.$(".carousel").carousel("pause");
  this.$(".carousel").data("bs.carousel",null);

// initialize normally because previous instance was killed
this.$(".carousel").carousel(...);

【讨论】:

非常感谢您的回答,对我帮助很大。我没有完全按照你的建议设置我的钩子,但我不太了解你的方式。这是适用于所有路由的通用路由器设置,还是我应该将此作为选项添加到该特定路由的 Router.map 部分? "loading" 是 iron-router 包提供的一个特殊的默认钩子,你可以在这里查看它的定义:github.com/EventedMind/iron-router/blob/devel/lib/client/… 它默认没有激活,所以你需要将它添加到 onBeforeAction 钩子中被您的路由器用作全局路由器设置。您还可以使用排除语法在某些路由上停用它。我无法在当前的 Iron-router DOCS.md 上找到这些解释,这曾经更清楚。 与 !项目声明 你的意思是一些 div、h1 等? 是的,只需将构成轮播项目的所有标签放在这里即可。 正确知道我遇到了很大的问题,似乎帮助者无法正常工作【参考方案2】:

我们最好在 #each 结束之后而不是 onRender 之后从模板中调用辅助处理程序。

一旦循环结束并加载相同的 DOM,处理程序就会调用该函数。

就像你的情况一样,每次将处理程序放置为initializeCarousel

#each featuredPosts ! item declaration /each initializeCarousel

现在在帮助器中将其定义为:

Template.carousel.helpers( // feed the #each with our cursor
    initializeCarousel: function()
        $('#featured-carousel').owlCarousel(
            loop:true, autoplay:true, autoplayTimeout:3000, items:1, smartSpeed:1080, padding:80 
        );
    );

这将使轮播在加载数据后加载。希望这会有所帮助。

【讨论】:

我在文档中读到不建议在帮助程序中进行这样的“设置”或“数据操作”。我认为是因为助手可能会连续多次被调用,这可能会导致不必要的副作用。

以上是关于Meteor:模板用数据渲染后调用函数的主要内容,如果未能解决你的问题,请参考以下文章

在 Meteor 的模板渲染函数中访问父数据上下文

ES6 angular-meteor ng-table getData 函数未调用

渲染后调用的函数反应原生

使用mvc7

fs.readFileSync 不是 Meteor 的函数,React

Meteor 渲染回调和应用 jQuery 插件