[SOLVED]Template.instance() returns null in onCreated() for templates in #each loop

I have a ‘posts’ template that gets displayed multiple times with an #each loop. In these templates’ onCreated() function, I want to access the meteor Template.instance() to use with jQuery to change some UI stuff. The problem is, this works perfectly for the first instance of the template ie.the one I display on top, but for the others, Template.instance() returns null and nothing works. I have tried every variation of this and I can’t seem to find an answer anywhere.

Here’s the code that doesn’t work:

Template.Challenge_Box.onCreated(function(){
    var self = this;
    self.subscribe('likes.document',self.data._id,function(){
        console.log(self);
        let likeState = Likes.find({},{user:{$eq:Meteor.userId()}}).count()>0?true:false;
        console.log(likeState);
        toggleLikeButton(likeState,self.$('#like-button'));
    });
});

And here’s the rest of the code:

Template.Challenge_Box.events({
    'click #like-button':function(event){
        //console.log($(event.target));
        if($(event.target).hasClass('liked')){
            //console.log('toggle: false');
            toggleLikeButton(false,event.currentTarget);
        }else{
            //console.log('toggle: true');
            toggleLikeButton(true,event.currentTarget);
        }
        console.log(Template.instance().data._id);
        Meteor.call('likes.toggle',Template.instance().data._id);
    }
});

function toggleLikeButton(toggle,target) {
    if(toggle){
        console.log('button-toggle: true, ',target);
        $(target).removeClass('btn-outline-danger');
        $(target).addClass('btn-danger');
        $(target).addClass('liked');
    }else{
        console.log('button-toggle: false, ',target);
        $(target).removeClass('btn-danger');
        $(target).addClass('btn-outline-danger');
        $(target).removeClass('liked');
    }
}

Hi @kevbot30001, welcome to the forums!

I’m having trouble understanding the issue you’re having.
Your description says Template.instance() returns null, but doesn’t appear in the code block that you say doesn’t work.
Would I be right in guessing that it’s the self.$('#like-button') part of that code block that doesn’t work?

If so, try changing #like-button to a class rather than an ID for elements that are rendered many times. The search algo for IDs will always return the first instance, since they are meant to be unique. When you are searching for it with Blaze, it’s possible it finds the first one, and checks if it’s inside the scope of the current template instance. Which is true for the first one and false for all subsequent ones.

Also, I suggest changing the onCreated to an onRendered, as it’s possible that if you have already subscribed to the same data that the callback runs immediately, before the template’s DOM has been rendered, and so all DOM queries will return null.

If my guess was wrong, are you talking about the call to Template.instance in the click handler?

Yeah, you guessed right. The arrow function in onCreated also was an issue for some reason, once I changed it to a regular function it worked. Otherwise changed to a class and it worked like a charm. There were 2 concurrent issues in my code and now both are fixed.

Thanks for the help!

Yeah, because arrow functions always keep the this from it’s parent (where it’s defined), it’s impossible to set it to something else with function.prototype.call or apply.
Which stops Blaze from setting this to the template instance, creating the problem.

But becomes useful in instances like the subscription callback, so you can use onCreated's this in there too:

this.subscribe('likes.document', this.data._id, () => {
  //...
  const likeButton = this.$('#like-button');
  toggleLikeButton(likeState, likeButton);
}

No need for self anymore!
(Of course, if it helps you / your team with readability, by all means keep using it)

You’re welcome!