Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meteor Animation

I am trying to implement an animation where an item animates to the left after it gets selected. The following code works. But only about 80% of the time.

JS

//myItem
Template.myItem.rendered = function(){
  if(Session.get("selected_item") === this.data._id){
    $(this.firstNode).addClass("selected");
  } else {
    $(this.firstNode).removeClass("selected");
  }
};

Template.myItem.events({
  "click .myItem": function(evt, template){
    Session.set("selected_item", this._id);
  }
});


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

Templates

<template name="myItem">
  <div class="myItem">{{name}}</div>
</template>

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

CSS

.myItem { transition: all 200ms 0ms ease-in; }
.selected { left: -20px; }

I also tried to wrap the code into a Meteor.defer() to make sure that really everything is ready to do the animation.

Template.myItem.rendered = function(){
  Meteor.defer(function() {
    if(Session.get("selected_item") === this.data._id){
      $(this.firstNode).addClass("selected");
    } else {
      $(this.firstNode).removeClass("selected");
    }
  });
};

But that results in this kind of error:

Exception from defer callback: TypeError {}

It would be great to see any ideas how to make the animation work every time.

UPDATE

Krab got the right answer. The exception was coming from the this reference. I want to add some details. So here are two working versions on how to do the animation:

with Meteor.defer()

Template.myItem.rendered = function(){
  var instance = this;
  if(Session.get("selected_item") === this.data._id){
    Meteor.defer(function() {  
      $(instance.firstNode).addClass("selected"); //use "instance" instead of "this"
    });
  }
};

(We don't actually need the else block here because meteor will remove the selected class if it redraws the item.)

or with $().animate()

Template.myItem.rendered = function(){
  if(Session.get("selected_item") === this.data._id){
    $(this.firstNode).animate({
      left: "-20px"
    }, 300);
  }
};

If you use the jQuery approach you need to remove the CSS code.

.myItem { transition: all 200ms 0ms ease-in; }
.selected { left: -20px; }

like image 374
Tim Bartsch Avatar asked Jul 18 '13 09:07

Tim Bartsch


1 Answers

try this, because this pointer in defer callback isn't the same as if the code is executed directly in rendered callback

Template.myItem.rendered = function(){
  var self = this;
  Meteor.defer(function() {
    if(Session.get("selected_item") === self.data._id){
      $(self.firstNode).addClass("selected");
    } else {
      $(self.firstNode).removeClass("selected");
    }
  });
};
like image 64
Krab Avatar answered Oct 13 '22 06:10

Krab