Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the parent template instance (of the current template)

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).

like image 759
Bogdan D Avatar asked Jan 14 '15 17:01

Bogdan D


3 Answers

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

like image 148
Bogdan D Avatar answered Nov 13 '22 18:11

Bogdan D


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;
    }
  }
});
like image 11
saimeunt Avatar answered Nov 13 '22 19:11

saimeunt


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();
  }
});
like image 9
Sacha Avatar answered Nov 13 '22 19:11

Sacha