How to cleanly re-run onRendered()?

templateInstance.view.template._callbacks.rendered[0].call(templateInstance);

Is there a cleaner way to access the onRendered() callback (to call it/them manually / to re-run them) given a template instance?

Using this.autorun that is based on reactive variables (ReactiveVar / Session / Collection)

2 Likes

I assume you’re trying to fix problems with someone else’s package, rather than trying to call a onRendered callback that you wrote yourself? Otherwise this seems like a very weird thing to do.

I have a template with jQuery UI widgets which I would like to reuse in other templates, some of which wrap/are jQuery UI dialog widgets. For the widgets to work, the dialog widget must be initialized first. The “obvious” place to put the dialog widget initialization is in the onRendered of the template containing the uninitialized dialog. The “obvious” place to put the other widget initialization is in the onRendered of the other template. However, doing this and nothing else, the contained template’s onRendered gets called first.

My idea of “obvious” might be wrong, but … it’s code which adds GUI functionality to the template as soon as the template is rendered to the window. Where else would the code reasonably go?

  • So this way onRendered waits and tries again if it wouldn’t work immediately because the initialization order would be incorrect. This seems like the best option. It keeps the onRendered code in an onRendered block, where it belongs semantically. It involves no communication between the dialog and its contents about the state of the dialog (unless you count the implicit “communication” here). And the init-order issue should be “solved” for the template.
Template.myTemplate.onRendered(function () {
  let templateInstance = this;
  if (templateInstance.$('*').parents('.dialog').not(':ui-dialog').length > 0) {
    Tracker.afterFlush(() => {
      templateInstance.view.template._callbacks.rendered[0].call(templateInstance);
    });
    return;
  }
...
  • I could wrap the onRendered callback function inside another function to pass around, but that doesn’t seem like an improvement.
Template.myTemplate.onRendered(function () {
  let myOnRenderedCallback = function () {
    ...
  };
  let templateInstance = this;
  if (templateInstance.$('*').parents('.dialog').not(':ui-dialog').length > 0) {
    Tracker.afterFlush(() => {
      myOnRenderedCallback.call(templateInstance);
    });
    return;
  }
});
  • Alternatively, “be explicit with the appendTo option” (e.g. Selectmenu appendTo). But that means remembering / documenting / communicating that all jQuery UI widgets must have their appendTo option set correctly if the widget is in a template which might wind up in a dialog.
templateInstance.$(selector).widget({
  appendTo: templateInstance.$('*').first()
...
  • Having the dialog template force it’s onRendered to fire before its contents’ onRendered callbacks might be better, but I couldn’t figure out how that might be accomplished and I’m not sure what other consequences that might incur.

  • It is possible to write a function to run every time a dialog is initialized to check all of its descendant elements for initialized widgets and updating their appendTo option to be the dialog unless the appendTo is already set to a descendant of the dialog getting initialized, but that seems slow and error-prone.