为什么在尝试为Meteor.js中的实时更改设置动画时,Template.mytemplate.rendered会被触发两次?

时间:2013-08-23 18:13:37

标签: javascript jquery meteor

我有一些问题可以确定Meteor.rendered的确如何运作。我正在尝试动画Meteor中的实时更改,非常像this question。每当我点击一个元素时,我都希望它闪烁。

但是,每次单击一个元素时,模板都会渲染两次,这反过来会触发动画两次。这是代码:

模板

<body>
  {{> myItemList}}
</body>

<template name="myItem">
  <div class="item {{selected}}">
    <h4>{{name}}</h4>
    <p>{{description}}</p>
  </div>
</template>

<template name="myItemList">
  {{#each items}}
    {{> myItem}}
  {{/each}}
</template>

的Javascript

Template.myItemList.helpers({
  items: function () {
    return Items.find();
  }
});

Template.myItem.helpers({
  selected: function () {
    return Session.equals('selected', this._id) ? "selected" : "";
  }
});

Template.myItem.events({
  'click .item' : function () {
    Session.set('selected', this._id);
  }
});

Template.myItem.rendered = function () {
  $(".item.selected").fadeOut().fadeIn();
};

我的理解是,每次调用Session.set时,都会重新使用Session.get用于同一个键的每个模板,如文档中所述。所以我的猜测是,不知怎的,Session.equals会导致模板重播2次?如果我使用此代码更改selected帮助程序:

Template.myItem.helpers({
  selected: function () {
    if (Session.get("selected")) === this._id)
      return "selected";
    else
      return "";
  }
});

然后动画被触发3次而不是2次。

考虑到所有这些,我的问题是:

  1. 为什么Template.myItem.rendered会被触发两次?幕后究竟发生了什么?
  2. 如何解决这个问题只能让我的动画触发一次?
  3. 答案详情

    所以评论@Xyand解决方案:

    1. 使用click将动画移动到Meteor.setTimeout()事件肯定有效,但对我来说感觉有些笨拙。
    2. 解决方案实际上很简单,已在this question中给出。代码如下所示:

      Template.myItem.rendered = function () {
        if (Session.equals('selected', this.data._id))
          $(this.firstNode).fadeOut().fadeIn();
      };
      
    3. 我首先尝试使用具有不同模板标记的不同项目并且它不起作用,这就是我创建这个最小项目以查看出错的原因。事实证明,为了实现这一点,您绝对需要在myItem模板中调用子模板myItemList。如果模板看起来像这样,

      <template name="myItemList">
        {{#each items}}
          <div class="item {{selected}}">
            <h4>{{name}}</h4>
            <p>{{description}}</p>
          </div>
        {{/each}}
      </template>
      

      因此为rendered模板调用了每个辅助函数和myItemList函数,代码将无法工作,因为在rendered函数内,模板实例将具有{ {1}}未定义的属性。

2 个答案:

答案 0 :(得分:1)

模板被渲染两次,因为渲染了同一模板的两个不同实例。一个将selected更改为"selected",另一个将其更改为""。这也解释了为什么在将equals切换为get时有3个渲染。

将命令式javascript与声明式混合总是一团糟。所以这是我的两个建议:

  1. if函数中添加rendered,以确保仅在所选项目模板实例中调用转换。

  2. 将转场移至事件本身。您可能需要使用Meteor.setTimeout

  3. 将转换置于rendered可能不是最佳选择,因为每次模板渲染时都会发生转换。例如,如果name发生变化。想想当代码变得更长时会发生什么......

答案 1 :(得分:0)

正如其他人所说,每次客户端获取另一部分数据时,模板都会被重新呈现,无论是因为数据发生了变化还是因为延迟。

值得庆幸的是,还有另一个在创建模板时只调用一次的函数,即 - template.created。它有一个缺点,你不能从那里访问视图元素,因为它们尚未创建。但是,您可以将模板标记为“新鲜”并在下一个渲染上安排动画:

var animate;

Template.myItem.created = function() {
    animate = true;
};

Template.myItem.rendered = function() {
    if(animate) {
        animate = false;
        ... // Do the animation here.
    }
};

<强>更新

Template.myItem.events({
  'click .item' : function () {
    animate = true;
    Session.set('selected', this._id);
  },
});