Meteor:在模板渲染完成后调用带数据的函数

3

我有许多帖子想要在走马灯中展示。为了使用走马灯,我使用 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;
};

我得到的结果是,Owl实际上认为我只有一个项目可以在走马灯中显示,这些项目是多个div。显然,在模板的#each部分完成或数据到达之前,Template.featuredCarousel.rendered内部的函数被调用。
我该如何使实例化轮播的函数仅在完全渲染包括所有数据的模板后才被调用?
非常感谢您的帮助。
附言:我使用iron-router进行路由,代码如下:
Router.map(function(){
this.route('home', {
    path: '/',
    waitOn: function(){
        return Meteor.subscribe('featured');
    },
    data: function(){
        return {featuredPosts: Featured.find({})};
    }
});
});

P.P.S.:我也尝试使用加载模板,但这并没有帮助。
2个回答

6
您已经准确地指出了问题,即:
似乎在 Template.featuredCarousel.rendered 内部的函数在模板的 #each 部分完成或数据到达之前被调用。
Template 的 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是否会正常运作(我快速查阅了文档,看起来还可以)。

对于类似的JS插件,如果没有提供reinit方法(例如bootstrap carousel),您可以先检查插件是否已被实例化,然后销毁并重新创建它,像这样:

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部分中的选项添加? - tomet
"loading"是iron-router包提供的特殊默认钩子,您可以在此处查看其定义:https://github.com/EventedMind/iron-router/blob/devel/lib/client/hooks.js#L27。它不会默认激活,因此您需要将其添加到Router使用的onBeforeAction钩子中作为全局路由器设置。您还可以使用排除语法在某些路由上停用它。我无法在当前的iron-router DOCS.md中找到这些解释,这曾经更清晰明了。 - saimeunt
使用{{!项目声明}},您是指一些div、h1等吗? - Ethaan
是的,请在此处放置任何标签,以组成您的轮播项。 - saimeunt
现在我遇到了非常大的问题,似乎助手没有起作用。 - Ethaan

0

最好我们在#each结束后直接从模板中调用helper处理程序,而不是在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 
        });
    });

这将使得轮播图在数据加载完成后再进行加载。希望这能有所帮助。


我在文档中读到,在helpers(帮助函数)中这样做"setup"或"data manipulations"不被推荐。我认为这是因为helpers可能会连续多次调用,这可能会导致不必要的副作用。 - evolross

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接