流星#each循环准备就绪
Meteor #each loop ready
我想知道有什么方法可以知道#each 循环是否 "ready"。 "ready" 我的意思是它渲染了所有节点并插入到 DOM 中。我什至不谈论 onRendered
回调(旧 rendered
)。我试过了
<template name="myTemplate">
<div class="some-class">
{{#if Template.subscriptionsReady}}
{{#each messages}}
<div>{{text}}</div>
{{/each}}
<script>
$(".some-class").trigger("LOOP_READY")
</script>
{{/if}}
</div>
</template>
Template.myTemplate.onRendered(function(){
this.$(".some-class").on("LOOP_READY", doSomething)
})
但是也不行。我不想使用定时器。
更新
messages
是当前对话的文本消息集合,dialogId
是存储在 Session
.
中的反应变量
Template.myTemplate.onCreated(function(){
var self = this;
self.autorun(function(){
self.subscribe("messages", Session.get("dialogId"))
})
})
因此,如果有人更改了 dialogId,myTemplate 会加载另一条对话消息,我想知道这些消息何时准备就绪,以便滚动到特定消息。简单 onReady
不起作用,因为在呈现所有消息并获得自己的高度之前我无法准确调用 scrollTop。
当空格键 {{#each}}
块完成渲染到 DOM 它所跨越的每个项目时,没有简单的方法来获得通知。
最好的解决方案是使用另一个反应式计算 (Tracker.autorun
) 来观察您的消息光标变化。
每次修改消息列表时,您都可以 运行 在所有其他反应性计算完成后执行任意代码,使用 Tracker.afterFlush
.
{{#each}}
块是其中一种计算,其作用是侦听您将其作为参数提供给它的反应式数据源,并重新呈现其 Template.contentBlock
与从源中获取的项目一样多的次数正在迭代,当前项目作为当前数据上下文。
通过侦听与 {{#each}}
块助手完全相同的反应式数据源并 运行 在您的代码完成其自身的反应式计算之后,您可以获得实际请求的行为而无需依赖一些奇怪的 setTimeout
技巧。
下面是这个模式的完整实现:
HTML
<template name="myTemplate">
<div class="some-class">
{{#if Template.subscriptionsReady}}
{{#each messages}}
<div class="message">{{text}}</div>
{{/each}}
{{/if}}
</div>
</template>
JS
// declare your reactive data source once to reuse the same in multiple places
function messagesCursor(){
return Messages.find();
}
Template.myTemplate.helpers({
messages: messagesCursor
});
Template.myTemplate.onRendered(function(){
this.autorun(function(){
// 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 messagesCount = messagesCursor().count();
//
Tracker.afterFlush(function(){
// assert that every messages have been rendered
console.log(this.$(".messages") == messagesCount);
}.bind(this));
}.bind(this));
});
@saimeunt 提供了优雅的解决方案,但我以其他方式实现了它,也许有人觉得它也有帮助。
由于我不希望每次收到新消息时都执行我的代码,因此我无法在自动运行完成后触发滚动。 (你会说,好吧,在仅取决于 dialogId
的自动运行中执行此操作。-然后我无法获得此对话框的确切消息数,因为订阅需要一些时间)
HTML
<template name="myTemplate">
<div class="chat">
{{#if Template.subscriptionsReady}}
<div class="messages">
{{#each messages}}
<div class="message">{{text}}</div>
{{/each}}
</div>
<script>$(".chat").trigger("MESSAGES_READY_EVENT")</script>
{{/if}}
</div>
</template>
JS
Template.chat.onCreated(function () {
var self = this;
self.messages = function() {
return Messages.find({dialogId:Session.get("dialogId")})
}
self.autorun(function(){
self.subscribe("messages", Session.get("dialogId"));
})
});
//helpers skipped
Template.chat.onRendered(function () {
var self = this;
self.$(".chat").on("MESSAGES_READY_EVENT", function () {
var computationNumber = 0;
var $messages = self.$(".messages");
//how many massages we have to render
var total = self.messages().count();
//max tries
var maxTries = 10;
var intervalId = Meteor.setInterval(function () {
var totalNodes = $messages.children().length;
if (computationNumber++ >= maxTries || total <= totalNodes) {
Meteor.clearInterval(intervalId);
$messages.scrollTop(someValue);
}
}, 100);
});
});
我想知道有什么方法可以知道#each 循环是否 "ready"。 "ready" 我的意思是它渲染了所有节点并插入到 DOM 中。我什至不谈论 onRendered
回调(旧 rendered
)。我试过了
<template name="myTemplate">
<div class="some-class">
{{#if Template.subscriptionsReady}}
{{#each messages}}
<div>{{text}}</div>
{{/each}}
<script>
$(".some-class").trigger("LOOP_READY")
</script>
{{/if}}
</div>
</template>
Template.myTemplate.onRendered(function(){
this.$(".some-class").on("LOOP_READY", doSomething)
})
但是也不行。我不想使用定时器。
更新
messages
是当前对话的文本消息集合,dialogId
是存储在 Session
.
Template.myTemplate.onCreated(function(){
var self = this;
self.autorun(function(){
self.subscribe("messages", Session.get("dialogId"))
})
})
因此,如果有人更改了 dialogId,myTemplate 会加载另一条对话消息,我想知道这些消息何时准备就绪,以便滚动到特定消息。简单 onReady
不起作用,因为在呈现所有消息并获得自己的高度之前我无法准确调用 scrollTop。
当空格键 {{#each}}
块完成渲染到 DOM 它所跨越的每个项目时,没有简单的方法来获得通知。
最好的解决方案是使用另一个反应式计算 (Tracker.autorun
) 来观察您的消息光标变化。
每次修改消息列表时,您都可以 运行 在所有其他反应性计算完成后执行任意代码,使用 Tracker.afterFlush
.
{{#each}}
块是其中一种计算,其作用是侦听您将其作为参数提供给它的反应式数据源,并重新呈现其 Template.contentBlock
与从源中获取的项目一样多的次数正在迭代,当前项目作为当前数据上下文。
通过侦听与 {{#each}}
块助手完全相同的反应式数据源并 运行 在您的代码完成其自身的反应式计算之后,您可以获得实际请求的行为而无需依赖一些奇怪的 setTimeout
技巧。
下面是这个模式的完整实现:
HTML
<template name="myTemplate">
<div class="some-class">
{{#if Template.subscriptionsReady}}
{{#each messages}}
<div class="message">{{text}}</div>
{{/each}}
{{/if}}
</div>
</template>
JS
// declare your reactive data source once to reuse the same in multiple places
function messagesCursor(){
return Messages.find();
}
Template.myTemplate.helpers({
messages: messagesCursor
});
Template.myTemplate.onRendered(function(){
this.autorun(function(){
// 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 messagesCount = messagesCursor().count();
//
Tracker.afterFlush(function(){
// assert that every messages have been rendered
console.log(this.$(".messages") == messagesCount);
}.bind(this));
}.bind(this));
});
@saimeunt 提供了优雅的解决方案,但我以其他方式实现了它,也许有人觉得它也有帮助。
由于我不希望每次收到新消息时都执行我的代码,因此我无法在自动运行完成后触发滚动。 (你会说,好吧,在仅取决于 dialogId
的自动运行中执行此操作。-然后我无法获得此对话框的确切消息数,因为订阅需要一些时间)
HTML
<template name="myTemplate">
<div class="chat">
{{#if Template.subscriptionsReady}}
<div class="messages">
{{#each messages}}
<div class="message">{{text}}</div>
{{/each}}
</div>
<script>$(".chat").trigger("MESSAGES_READY_EVENT")</script>
{{/if}}
</div>
</template>
JS
Template.chat.onCreated(function () {
var self = this;
self.messages = function() {
return Messages.find({dialogId:Session.get("dialogId")})
}
self.autorun(function(){
self.subscribe("messages", Session.get("dialogId"));
})
});
//helpers skipped
Template.chat.onRendered(function () {
var self = this;
self.$(".chat").on("MESSAGES_READY_EVENT", function () {
var computationNumber = 0;
var $messages = self.$(".messages");
//how many massages we have to render
var total = self.messages().count();
//max tries
var maxTries = 10;
var intervalId = Meteor.setInterval(function () {
var totalNodes = $messages.children().length;
if (computationNumber++ >= maxTries || total <= totalNodes) {
Meteor.clearInterval(intervalId);
$messages.scrollTop(someValue);
}
}, 100);
});
});