简体   繁体   English

尝试在Meteor.js中对实时更改进行动画处理时,为什么会触发Template.mytemplate.rendered两次?

[英]Why is Template.mytemplate.rendered getting triggered twice when trying to animate live changes in Meteor.js?

I have some issues figuring out how Meteor.rendered works exactly. 我在弄清楚Meteor.rendered工作原理时Meteor.rendered一些问题。 I'm trying to animate a live change in Meteor, very much like this question does. 我正在尝试制作流星动画的动画,就像这个问题一样。 Whenever I click on an element, I want it to blink. 每当我单击某个元素时,我都希望它闪烁。

However, the template gets rendered twice every time an element is clicked, which in turn triggers the animation two times. 但是,每次单击一个元素时,模板都会渲染两次,从而又两次触发动画。 Here is the code: 这是代码:

Template 模板

<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 使用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();
};

My understanding is that every time Session.set is called, every template that uses Session.get for the same key is re-runed, as explained in the documentation. 我的理解是,每次调用Session.set ,都会重新运行每个使用Session.get作为同一键的模板,如文档中所述。 So my guess is that somehow, Session.equals causes 2 reruns of the template, maybe? 所以我的猜测是,以某种方式, Session.equals可能导致模板的2次重新运行。 If I change the selected helper with this code: 如果我使用以下代码更改selected帮助器:

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

Then the animation gets triggered 3 times instead of 2. 然后动画将触发3次而不是2次。

With all that in mind, my questions are: 考虑到所有这些,我的问题是:

  1. Why is Template.myItem.rendered getting triggered twice? 为什么Template.myItem.rendered被触发两次? What exactly goes on behind the scenes? 幕后到底发生了什么?
  2. How can I fix this to only have my animation triggered once? 如何解决此问题,使动画仅触发一次?

ANSWER DETAILS 答案详情

So to comment on @Xyand solutions: 因此,对@Xyand解决方案发表评论:

  1. Moving the animations to the click event with a Meteor.setTimeout() definitely works, but felt a bit hacky to me. 使用Meteor.setTimeout()将动画移至click事件绝对可以,但是对我来说有点Meteor.setTimeout()
  2. The solution was actually quite simple, and already given in this question . 解决方案实际上非常简单,并且已经在此问题中给出了。 The code looks like this: 代码如下:

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

I first tried that on a different project that had a different template markup and it didn't work, this is why I created this minimal project to see where things went wrong. 我首先在一个具有不同模板标记的不同项目上尝试了该项目,但该项目不起作用,这就是为什么我创建此最小项目以查看问题出在哪里的原因。 Turns out that for this to work, you absolutely need to have a sub-template myItem called inside the myItemList template. 事实证明,要使其正常工作,您绝对需要在myItemList模板内部调用一个子模板myItem If the template looks like this, 如果模板看起来像这样,

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

and thus every helper function and the rendered function are called for the myItemList template, the code won't work because inside the rendered function, the template instance will have a .data attribute that is undefined. 因此, myItemList模板调用每个辅助函数和rendered函数,该代码将不起作用,因为在rendered函数内部,模板实例将具有未定义的.data属性。

The template gets rendered twice because two different instances of the same template get rendered. 该模板被渲染两次,因为同一模板的两个不同实例被渲染。 One that changes selected to "selected" and another that changes it to "" . 一个将selected更改为"selected" ,另一个将其更改为"" This also explains why you have 3 renders when you switch equals to get . 这也解释了为什么在切换equals get时有3个渲染。

Mixing imperative javascript with declarative is always a mess. 将命令性javascript与声明性混合总是一团糟。 So here are my two suggestions: 所以这是我的两个建议:

  1. Add an if inside the rendered function to make sure you call the transition only in the selected item template instance. rendered函数内添加if ,以确保仅在所选项目模板实例中调用转换。

  2. Move the transition to the event itself. 将过渡移到事件本身。 You might need to use Meteor.setTimeout . 您可能需要使用Meteor.setTimeout

Placing the transition in rendered might not be the best option as it will happen every time the template renders. 将过渡放置在rendered可能不是最佳选择,因为每次模板渲染时都会发生。 For instance if name changes. 例如,如果name更改。 Think what would happen when the code gets longer... 想一想当代码变长时会发生什么...

As others have said, template gets rerendered every time client fetch another portion of data, whether it's because the data has changed or because of latency. 正如其他人所说,每次客户端获取另一部分数据时,模板都会重新呈现,无论是因为数据已更改还是由于延迟。

Thankfully, there's another function that's only called once when the template is created, that is - template.created . 值得庆幸的是,还有一个函数在创建模板时仅被调用一次,即template.created It has the downside that you cannot access view elements from there, as they weren't yet created. 缺点是您无法从那里访问视图元素,因为它们尚未创建。 You can, however, mark the template as "fresh" and schedule animation on the next render: 但是,您可以将模板标记为“新鲜”,并在下一个渲染中安排动画:

var animate;

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

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

Update: 更新:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM