How to get the parent template instance (of the current template)
Asked Answered
C

5

19

Is there a clean way to get the parent template of the current template? Nothing is officially documented in Meteor's API. I'm talking about the Blaze.TemplateInstance, not the context (i.e. not Template.parentData).

Cartload answered 14/1, 2015 at 17:56 Comment(0)
C
31

In the end, I've extended the template instances similarly with Meteor's parentData, like this:

/**
 * Get the parent template instance
 * @param {Number} [levels] How many levels to go up. Default is 1
 * @returns {Blaze.TemplateInstance}
 */

Blaze.TemplateInstance.prototype.parentTemplate = function (levels) {
    var view = this.view;
    if (typeof levels === "undefined") {
        levels = 1;
    }
    while (view) {
        if (view.name.substring(0, 9) === "Template." && !(levels--)) {
            return view.templateInstance();
        }
        view = view.parentView;
    }
};

Example usage: someTemplate.parentTemplate() to get the immediate parent

Cartload answered 15/1, 2015 at 11:45 Comment(5)
I had to change "var view = Blaze.currentView" to "var view = this.view;" otherwise "someTemplate.parentTemplate(2) != someTemplate.parentTemplate().parentTemplate()"Ottilie
good point, @GraemePyle. I haven't used Blaze in almost a year (and don't remember if view was available in TemplateInstance back then), but I took your word for it and edited the answer. CheersCartload
I can verify that this code works to access the parent template instance. It is the actual instance including any params attached to the parent instance when it originally loaded. Wonder why Blaze doesn't have an official API for this? They only have it so you can access parentData but not the instance. Hope this doesn't break in future updates to Blaze.Excurvature
Great way to fill the hole in the Blaze API, and it does work as-is. I was uneasy about relying on the template name string compare for this to work, so I recommend changing view.name.substring(0, 9) === "Template." to view.hasOwnProperty('template')Private
sounds good, @cstricklan, feel free to edit the answer. I also like checking view.template more, but I won't change the answer myself, because I haven't used blaze in more than a yearCartload
T
11

Is there a clean way to get the parent template of the current template?

Currently, none that I know of, but this is supposed to happen sometime in the future as part of a planned "better API for designing reusable components" (this is discussed in the Meteor post 1.0 roadmap).

For the moment, here is a workaround I'm using in my projects :

// extend Blaze.View prototype to mimick jQuery's closest for views
_.extend(Blaze.View.prototype,{
    closest:function(viewName){
        var view=this;
        while(view){
            if(view.name=="Template."+viewName){
                return view;
            }
            view=view.parentView;
        }
        return null;
    }
});

// extend Blaze.TemplateInstance to expose added Blaze.View functionalities
_.extend(Blaze.TemplateInstance.prototype,{
    closestInstance:function(viewName){
        var view=this.view.closest(viewName);
        return view?view.templateInstance():null;
    }
});

Note that this is only supporting named parent templates and supposed to work in the same fashion as jQuery closest to traverse parent views nodes from a child to the top-most template (body), searching for the appropriately named template.

Once this extensions to Blaze have been registered somewhere in your client code, you can do stuff like this :

HTML

<template name="parent">
  <div style="background-color:{{backgroundColor}};">
    {{> child}}
  </div>
</template>

<template name="child">
  <button type="button">Click me to change parent color !</button>
</template>

JS

Template.parent.created=function(){
  this.backgroundColor=new ReactiveVar("green");
};

Template.parent.helpers({
  backgroundColor:function(){
    return Template.instance().backgroundColor.get();
  }
});

Template.child.events({
  "click button":function(event,template){
    var parent=template.closestInstance("parent");
    var backgroundColor=parent.backgroundColor.get();
    switch(backgroundColor){
      case "green":
        parent.backgroundColor.set("red");
        break;
      case "red":
        parent.backgroundColor.set("green");
        break;
    }
  }
});
Transit answered 14/1, 2015 at 18:27 Comment(1)
Thanks for the elaborate answer @saimeunt! Not exactly what I wanted, but useful nevertheless. I had already built a function that works like meteor's parentData, but I'm upvoting your answer, because closest is also a useful patternCartload
D
9

What I've been doing so far is that if I need to access the parent instance in a child template's function, I try to instead refactor this function to declare it on the parent template, and then pass it as argument to the child, who can then execute it.

As an example, let's say I want to increment a template variable on the parent template from within the child template. I could write something like this:

Template.parentTemplate.onCreated(function () {
  var parentInstance = this;
  parentInstance.count = new ReactiveVar(1);
});

Template.parentTemplate.helpers({
  incrementHandler: function () {
    var parentInstance = Template.instance();
    var count = parentInstance.count.get();

    return function () {
      var newCount = count + 1;
      parentInstance.count.set(newCount);
    };
  }
});

Then include my child template:

{{> childTemplate handler=loadMoreHandler}}

And set up my event:

Template.childTemplate.events({
  'click .increment-button': function (event, childInstance) {
    event.preventDefault();
    childInstance.data.handler();
  }
});
Diacritic answered 5/8, 2015 at 5:3 Comment(0)
P
3

If you don't want to extend Blaze.TemplateInstance you can access the parent instance like this:

Template.exampleTemplate.onRendered(function () {
  const instance = this;
  const parentInstance = instance.view.parentView.templateInstance();
});

Only tested in Meteor 1.4.x

Phaedra answered 2/3, 2017 at 22:27 Comment(0)
M
1

You can use a package like Aldeed's template-extension

The following method is available there:

templateInstance.parent(numLevels, includeBlockHelpers)
Meneau answered 14/3, 2018 at 22:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.