Maximus S
Maximus S

Reputation: 11095

Run a function after DOM is updated from a helper function

Edit:

This is precisely what I want to do:

  Template.FrameItems.helpers({
    frames: function() {
      var trialId = Session.get('trialId');
      return Frames.find({trialId: trialId});
      // when new frames are rendered, I want to call positionElements()
    }
  });

  Template.FrameItems.onRendered(function() {
    this.autorun(function() {
      var trialId = Session.get("trialId");
      positionElements();
      // problem: positionElements() is called before the DOM is updated from `frames` helper function
    })
  });

EDIT2:

This is my second attempt which doesn't work.

  var frameDep = new Tracker.Dependency;
  Template.FrameItems.helpers({
    frames: function() {
      var trialId = Session.get('trialId');
      frameDep.changed();
      return Frames.find({trialId: trialId});
      // when new frames are rendered, I want to call positionElements()
    }
  });

  Template.FrameItems.onRendered(function() {
    this.autorun(function() {
      frameDep.depend();
      positionElements();
    });

The same problem still remains. By the time positionElements() is invoked, the DOM is still not updated with the new frames objects. I need a way to find out when the DOM is updated. onRendered() is not called after the very first time the template is rendered, which is problematic in my case.

EDIT3:

I ended up doing this, but I feel like there should be a better solution.

if (Meteor.isClient) {
  var frameItemsTemplate;
  Template.TrialWorkSpace.onRendered(function() {
    this.autorun(function() {
      var trialId = Session.get("trialId");
      if (frameItemsTemplate) {
        Blaze.remove(frameItemsTemplate);
      }
      frameItemsTemplate = Blaze.render(Template.FrameItems,
        $('.frame-items-container')[0]);
    });
  });

  Template.FrameItems.helpers({
    frames: function() {
      var trialId = Session.get('trialId');
      return Frames.find({trialId: trialId});
    }
  });

  Template.FrameItems.onRendered(function() {
    positionElements();
  });
}

And the template file

<template name="TrialWorkSpace">
  <div class="trial-workspace-container">  
    <div class="row frame-items-container">
      <!-- populated programmatically instead of {{> FrameItems}} -->
    </div>
  </div>
</template>

<template name="FrameItems">
  {{#each frames}}
    <div id="frame-{{_id}}" class="frame-preview-item cyan lighten-5">
      <div class='frame-name'>{{name}}</div>
      <div class="ep"></div>
    </div>
  {{/each}}
</template>

Upvotes: 1

Views: 817

Answers (2)

Zahed
Zahed

Reputation: 73

I have solved following way:

Template.TrialWorkSpace.onRendered(function() {
    var self = this;
    self.autorun(function(){
        var trialId = Session.get("trialId");
        Tracker.afterflush(function(){
           positionElements();
        }.bind(self));
    })
});

when Session.get("trialId") is set or change than it will call positionElements() after dom refresh.

Upvotes: 0

Matt K
Matt K

Reputation: 4948

Your first assumption is wrong. onRendered only renders when the template is inserted into the DOM, if you want reactivity, you'll wanna stick an autorun in the callback.

if (Meteor.isClient) {
  Template.TrialWorkSpace.onCreated({
    dep = new Tracker.Dependency();
  });
  Template.FrameItems.helpers({
    frames: function() {
      var trialId = Session.get('trialId');
      console.log("I am getting called");
      dep.changed();
      return Frames.find({trialId: trialId});
    }
  });

  Template.TrialWorkSpace.onRendered(function() {
    Tracker.autorun(function() {
      dep.depend();
      console.log("onRendered");
    })
  })
}

Upvotes: 1

Related Questions