#each 完成后如何执行回调?
How to execute a callback after an #each is done?
我在 #each
完成后遇到回调问题。我有一个名为 "content":
的模板
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
{{else}}
Loading...
{{/if}}
</template>
起初我等待订阅,当它可用时,我用 {{#each}}
遍历我的 Collection 并附加 div
。我需要的是 for-each 循环完成时的一种回调(换句话说 DOM 就绪)。
Template.content.onRendered()
-> 提前触发
我还尝试在 {{each}} 之后附加一张图片并在其 onload
中触发一个函数,如下所示:
<img style="height:0;width:0" src="*mysource*" onload="callback()">
-> 有时确实有效但不知何故不可靠
有没有办法得到这个回调?如果能带来解决方案,我不怕更改此模板的结构。
当空格键 {{#each}}
块完成渲染到 DOM 每个迭代的项目时,没有简单的方法来获得通知。
最佳解决方案是使用另一个反应式计算 (Tracker.autorun
) 来观察您的(反应式)当前数据。
每次修改当前数据(可能是游标)时,您都可以 运行 在所有其他反应性计算完成后执行任意代码,使用 Tracker.afterFlush
.
{{#each}}
块是其中一种计算,其作用是侦听您将其作为参数提供给它的反应式数据源,并重新呈现其 Template.contentBlock
与从源中获取的项目一样多的次数正在迭代,当前项目作为当前数据上下文。
通过侦听与 {{#each}}
块助手完全相同的反应式数据源并 运行 在您的代码完成其自身的反应式计算后,您可以获得实际请求的行为而无需依赖一些奇怪的技巧。
下面是这个模式的完整实现:
JS
Template.content.helpers({
currentData: function(){
return Template.currentData();
}
});
Template.content.onRendered(function(){
this.autorun(function(){
var cursor = Template.currentData();
// we need to register a dependency on the number of documents returned by the
// cursor to actually make this computation rerun everytime the count is altered
var count = cursor.count();
//
Tracker.afterFlush(function(){
// assert that every items have been rendered
console.log(this.$("[data-cid]") == count);
}.bind(this));
}.bind(this));
});
您还可以使用子模板并计算子模板呈现的数量。如果此数字是集合中的项目数,则将呈现所有项目。
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
{{> showData}}
{{/each}}
{{else}}
Loading...
{{/if}}
</template>
<template name="currentData">
<div data-cid="{{this._id}}"></div>
</template>
这样,初始化一个反应变量并跟踪它:
var renderedCount = new ReactiveVar(0);
Tracker.autorun(function checkIfAllRendered() {
if(renderedCount.get() === currentData.count() && renderedCount.get() !== 0) {
//allDataRendered();
}
});
当 currentData
模板被渲染时,递增它,当它被销毁时递减它。
Template.currentData.onRendered(function() {
renderedCount.set(++renderedCount.curValue);
});
Template.currentData.onDestroyed(function() {
renderedCount.set(--renderedCount.curValue);
});
考虑一种可能更简单的方法 - 在您的 #each
块周围创建一个模板,然后在之后获得一个 onRendered 事件:
html:
<template name="content">
{{#if Template.subscriptionsReady}}
{{> eachBlock}}
{{else}}
Loading...
{{/if}}
</template>
<template name="eachBlock">
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
</template>
js:
Template.eachBlock.onRendered(function(){
console.log("My each block should now be fully rendered!");
});
我遇到了类似的问题,经过大量搜索后找到了以下解决方案。我尝试使用 Tracker
、onRendered
和其他技巧,其中 none 有效。这可以被认为更像是一种 hack,但有效。不幸的是不记得我最初是在哪里找到这个解决方案的。
从您的模板开始,但在您的 each
之后添加一个模板标签。
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
{{doneTrigger}}
{{else}}
Loading...
{{/if}}
</template>
然后定义一个returns null.
的助手
Template.content.helpers({
doneTrigger: function() {
Meteor.defer(function() {
// do what you need to do
});
return null;
}
});
你可以阅读更多关于Meteor.defer()
here,但它相当于使用0毫秒setTimeout
。
我在 #each
完成后遇到回调问题。我有一个名为 "content":
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
{{else}}
Loading...
{{/if}}
</template>
起初我等待订阅,当它可用时,我用 {{#each}}
遍历我的 Collection 并附加 div
。我需要的是 for-each 循环完成时的一种回调(换句话说 DOM 就绪)。
Template.content.onRendered()
-> 提前触发
我还尝试在 {{each}} 之后附加一张图片并在其 onload
中触发一个函数,如下所示:
<img style="height:0;width:0" src="*mysource*" onload="callback()">
-> 有时确实有效但不知何故不可靠
有没有办法得到这个回调?如果能带来解决方案,我不怕更改此模板的结构。
当空格键 {{#each}}
块完成渲染到 DOM 每个迭代的项目时,没有简单的方法来获得通知。
最佳解决方案是使用另一个反应式计算 (Tracker.autorun
) 来观察您的(反应式)当前数据。
每次修改当前数据(可能是游标)时,您都可以 运行 在所有其他反应性计算完成后执行任意代码,使用 Tracker.afterFlush
.
{{#each}}
块是其中一种计算,其作用是侦听您将其作为参数提供给它的反应式数据源,并重新呈现其 Template.contentBlock
与从源中获取的项目一样多的次数正在迭代,当前项目作为当前数据上下文。
通过侦听与 {{#each}}
块助手完全相同的反应式数据源并 运行 在您的代码完成其自身的反应式计算后,您可以获得实际请求的行为而无需依赖一些奇怪的技巧。
下面是这个模式的完整实现:
JS
Template.content.helpers({
currentData: function(){
return Template.currentData();
}
});
Template.content.onRendered(function(){
this.autorun(function(){
var cursor = Template.currentData();
// we need to register a dependency on the number of documents returned by the
// cursor to actually make this computation rerun everytime the count is altered
var count = cursor.count();
//
Tracker.afterFlush(function(){
// assert that every items have been rendered
console.log(this.$("[data-cid]") == count);
}.bind(this));
}.bind(this));
});
您还可以使用子模板并计算子模板呈现的数量。如果此数字是集合中的项目数,则将呈现所有项目。
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
{{> showData}}
{{/each}}
{{else}}
Loading...
{{/if}}
</template>
<template name="currentData">
<div data-cid="{{this._id}}"></div>
</template>
这样,初始化一个反应变量并跟踪它:
var renderedCount = new ReactiveVar(0);
Tracker.autorun(function checkIfAllRendered() {
if(renderedCount.get() === currentData.count() && renderedCount.get() !== 0) {
//allDataRendered();
}
});
当 currentData
模板被渲染时,递增它,当它被销毁时递减它。
Template.currentData.onRendered(function() {
renderedCount.set(++renderedCount.curValue);
});
Template.currentData.onDestroyed(function() {
renderedCount.set(--renderedCount.curValue);
});
考虑一种可能更简单的方法 - 在您的 #each
块周围创建一个模板,然后在之后获得一个 onRendered 事件:
html:
<template name="content">
{{#if Template.subscriptionsReady}}
{{> eachBlock}}
{{else}}
Loading...
{{/if}}
</template>
<template name="eachBlock">
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
</template>
js:
Template.eachBlock.onRendered(function(){
console.log("My each block should now be fully rendered!");
});
我遇到了类似的问题,经过大量搜索后找到了以下解决方案。我尝试使用 Tracker
、onRendered
和其他技巧,其中 none 有效。这可以被认为更像是一种 hack,但有效。不幸的是不记得我最初是在哪里找到这个解决方案的。
从您的模板开始,但在您的 each
之后添加一个模板标签。
<template name="content">
{{#if Template.subscriptionsReady}}
{{#each currentData}}
<div data-cid="{{this._id}}"></div>
{{/each}}
{{doneTrigger}}
{{else}}
Loading...
{{/if}}
</template>
然后定义一个returns null.
的助手Template.content.helpers({
doneTrigger: function() {
Meteor.defer(function() {
// do what you need to do
});
return null;
}
});
你可以阅读更多关于Meteor.defer()
here,但它相当于使用0毫秒setTimeout
。