Meteor:使用数据渲染模板后调用函数 [英] Meteor: Call function after template is rendered with data
问题描述
我想在轮播中显示一些帖子.对于轮播,我使用 OwlCarousel .
{{#每个精选帖子}}<div><h2>{{帖子标题}}{{/每个}}
我这样称呼我的轮播:
Template.featuredCarousel.rendered = function(){$('#featured-carousel').owlCarousel({循环:真,自动播放:真,自动播放超时:3000,项目:1,智能速度:1080,填充:80});this.rendered = true;};
我得到的结果是 Owl 基本上认为我只有一个项目要显示在多个 div 的轮播中.显然发生的是 Template.featuredCarousel.rendered 中的函数在模板的 #each-part 完成之前或数据到达之前被调用.
如何使实例化轮播的函数仅在模板完全呈现包括所有数据后才被调用?
非常感谢您的帮助.
P.S.:我使用 Iron-router 进行路由,如下所示:
Router.map(function(){this.route('家', {小路: '/',等待:功能(){return Meteor.subscribe('精选');},数据:函数(){返回 {featuredPosts: Featured.find({})};}});});
P.P.S.:我也尝试过使用加载模板,但这也无济于事.
您已经正确地指出了您的问题:
<块引用>显然发生的是 Template.featuredCarousel.rendered 中的函数在模板的 #each-part 完成之前或数据到达之前被调用.
模板的 rendered
回调仅在您的模板实例第一次插入 DOM 时调用一次,因此如果您的数据尚未准备好(从服务器获取)但 #each 块将成功t 生成任何 HTML 元素,当您实例化轮播时,它会显示为空.
您可以做的是确保您的数据在您的 rendered
回调触发之前准备就绪.显然你已经尝试设置这个解决方案但没有运气,你确定你像这样添加了默认加载钩子吗?
Router.onBeforeAction("loading");
更好的解决方案是在呈现的回调中监听数据库更改,并在第一次获取项目时相应地重新初始化轮播,然后动态添加和/或删除.
HTML
<div class="owl-carousel">{{#每个精选帖子}}{{!物品声明}}{{/每个}}
模板>
JS
function featuresPosts(){返回 Featured.find();}Template.carousel.helpers({//用我们的光标输入#each精选帖子:精选帖子});Template.carousel.rendered=function(){var 选项={...};//第一次初始化this.$(".owl-carousel").owlCarousel(options);this.autorun(_.bind(function(){//这就是我们监听"数据库变化的方式:我们设置了一个反应式计算//在内部我们从数据库中引用一个游标(查询)并注册//通过调用 cursor.forEach 依赖它,所以每当找到文档时//由游标在服务器上修改,计算重新运行//更新的内容,请注意我们使用了与 #each 相同的 CURSORvar post=featuredPosts();//forEach 像#each 一样注册对游标内容的依赖post.forEach(function(post){...});//最后我们需要重新初始化轮播,以便它考虑到我们新添加的//HTML 元素作为轮播项目,但我们只能在 #each 块之后这样做//已经完成插入到 DOM 中,这就是我们必须使用 Deps.afterFlush 的原因//#each 块本身设置了另一个计算并且 Deps.afterFlush 确保//此计算在执行回调之前完成Tracker.afterFlush(_.bind(function(){this.$(".owl-carousel").data("owlCarousel").reinit(options);},这));},这));};
我对 owl-carousel 不熟悉,所以我不确定 reinit 是否会正常运行(我已经快速检查了文档,但似乎没问题).
对于没有 reinit 方法的类似 JS 插件(例如 bootstrap carousel),你可以先检查插件是否已经实例化,然后销毁它并像这样重新创建它:
var carousel=this.$(".carousel").data("bs.carousel");如果(轮播){//bootstrap carousel 也没有销毁,所以我们通过暂停它来模拟它//并将数据属性重置为空(有点乱)this.$(".carousel").carousel("暂停");this.$(".carousel").data("bs.carousel",null);}//正常初始化,因为前一个实例被杀死this.$(".carousel").carousel({...});
I have a number of posts that I want to display inside of a carousel. For the carousel, I use OwlCarousel .
<div class="owl-carousel" id="featured-carousel">
{{#each featuredPosts}}
<div>
<h2>
{{postTitle}}
</h2>
</div>
{{/each}}
</div>
I call my carousel like so:
Template.featuredCarousel.rendered = function(){
$('#featured-carousel').owlCarousel({
loop:true,
autoplay:true,
autoplayTimeout:3000,
items:1,
smartSpeed:1080,
padding:80
});
this.rendered = true;
};
The result I get is that Owl basically thinks that I just have one item to display in the carousel which are multiple divs. What apparently happens is that the function inside Template.featuredCarousel.rendered is called before the #each-part of the template is completed or before the data has arrived.
How can I make the function instantiating the carousel only be called after the template is fully rendered including all data?
Thank you very much for your help.
P.S.: I use iron-router for routing like so:
Router.map(function(){
this.route('home', {
path: '/',
waitOn: function(){
return Meteor.subscribe('featured');
},
data: function(){
return {featuredPosts: Featured.find({})};
}
});
});
P.P.S.: I have also tried using a loading template but that doesn't help either.
You have identified your problem correctly by stating that :
What apparently happens is that the function inside Template.featuredCarousel.rendered is called before the #each-part of the template is completed or before the data has arrived.
The rendered
callback of a Template is only called once when your template instance is first inserted in the DOM, so if your data is not ready (fetched from server) yet the #each block won't generate any HTML elements and when you instantiate your carousel it will appear empty.
What you can do is make sure your data is ready before your rendered
callback fires.
Apparently you've tried to setup this solution with no luck, are you sure you added the default loading hook like so ?
Router.onBeforeAction("loading");
An even better solution is to listen for database changes in your rendered callback and reinitialize your carousel accordingly when items are first fetched, and then dynamically added and/or removed.
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));
};
I'm not familiar with owl-carousel so I'm not sure if reinit will act properly (I've quickly checked the documentation and is seems OK though).
For similar JS plugins where a reinit method is not available (bootstrap carousel for example), you can first check if the plugin has been instantiated then destroy it and recreate it like this :
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({...});
这篇关于Meteor:使用数据渲染模板后调用函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!