2

我有一些问题弄清楚如何Meteor.rendered工作。我正在尝试为 Meteor 中的实时变化设置动画,就像这个问题一样。每当我点击一个元素时,我希望它闪烁。

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

模板

<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. 我怎样才能解决这个问题,让我的动画只触发一次?

回答详情

所以评论@Xyand解决方案:

  1. 将动画移动到click事件中Meteor.setTimeout()绝对有效,但对我来说有点hacky。
  2. 解决方案实际上非常简单,并且已经在这个问题中给出。代码如下所示:

    Template.myItem.rendered = function () {
      if (Session.equals('selected', this.data._id))
        $(this.firstNode).fadeOut().fadeIn();
    };
    

我首先在一个具有不同模板标记的不同项目上进行了尝试,但它不起作用,这就是我创建这个最小项目以查看哪里出了问题的原因。事实证明,要使其正常工作,您绝对需要在模板myItem内调用一个子myItemList模板。如果模板看起来像这样,

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

因此renderedmyItemList模板调用每个帮助函数和函数,代码将不起作用,因为在rendered函数内部,模板实例将具有.data未定义的属性。

4

2 回答 2

1

模板被渲染两次,因为同一模板的两个不同实例被渲染。一个更改selected"selected",另一个更改为""。这也解释了为什么当你切换equalsget.

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

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

  2. 将过渡移动到事件本身。您可能需要使用Meteor.setTimeout.

放置过渡rendered可能不是最佳选择,因为每次模板呈现时都会发生这种情况。例如如果name发生变化。想想当代码变长时会发生什么......

于 2013-08-23T19:35:42.203 回答
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);
  },
});
于 2013-08-23T20:29:21.963 回答