流星#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);
    });
});