Meteor: Access Template Helper (or variable) from another helper
Asked Answered
C

8

22

How can I reference a template helper from another one? For example...

Template.XXX.helpers({
    reusableHelper: function() {
        return this.field1 * 25 / 100; //or some other result
    },
    anotherHelper: function() {
        if (this.reusableHelper() > 300) //this does not work
            return this.reusableHelper() + ' is greater than 300'; 
        else
            return this.reusableHelper() + ' is smaller than 300';
    }
});

I have also tried Template.instance().__helpers.reusableHelper - all with no luck.

Alternatively is there a way to define reactive Template instance variables?

XXX is a sub-template that renders multiple times on the same page.

Clinkerbuilt answered 29/1, 2015 at 10:6 Comment(0)
A
12

This like using of common code, you can make another javascript function which contains the your reusable code and call it from wherever you required.

Like in your code-

function calcField(field){
   return field * 25 / 100
}

and in you template helper-

Template.XXX.helpers({
    reusableHelper: function() {
        return calcField(this.field1); 
    },
    anotherHelper: function() {
        if (calcField(this.field1) > 300) 
            return calcField(this.field1) + ' is greater than 300'; 
        else
            return calcField(this.field1) + ' is smaller than 300';
    }
});

and

Alternatively is there a way to define reactive Template instance variables?

you can use Session variables or Reactive variable

Annihilation answered 29/1, 2015 at 10:19 Comment(1)
I think managing that large a number of Session variables would not be too wise - maybe a local collection would be better. With that said, I think your answer is a great option. Should have thought of it :)Clinkerbuilt
J
14

You can but only with global template helpers.

Blaze._globalHelpers.nameOfHelper()

Here is an example calling Iron:Router's pathFor global helper.

Template.ionItem.helpers({
  url: function () {
    var hash = {};
    hash.route = path;
    hash.query = this.query;
    hash.hash = this.hash;
    hash.data = this.data;
    var options = new Spacebars.kw(hash);

    if (this.url){
      return Blaze._globalHelpers.urlFor(options)
    } else if( this.path || this.route ) {
      return Blaze._globalHelpers.pathFor(options)
    }
  }
});

EDIT: To your second question. You can call the same template as many times as you like on a page and pass different data attributes directly into it and/or use #each block template wrapper to iterate over data. #each will call a template many times giving it a different data context each time.

#each Example

<template name="listOfPosts">
  <ul>
    {{#each posts}}
      {{>postListItem}} <!--this template will get a different data context each time-->
    {{/each}}
  </ul>
</template>

Attributes Example

<template name="postDetails">
  {{>postHeader title="Hello World" headerType="main" data=someHelper}}
  {{>postHeader title="I am a sub" headerType="sub" data=newHelper}}
  {{>postBody doc=bodyHelper}}
</template>
Jamisonjammal answered 29/1, 2015 at 10:41 Comment(1)
I think you're right...I could pass the reusableHelper as an argument to the subtemplate...In other words {{each ...}} {{>XXX percentage=reusableHelper}} {{/each}} - and in the sub-template reference the parameter. ThanksClinkerbuilt
A
12

This like using of common code, you can make another javascript function which contains the your reusable code and call it from wherever you required.

Like in your code-

function calcField(field){
   return field * 25 / 100
}

and in you template helper-

Template.XXX.helpers({
    reusableHelper: function() {
        return calcField(this.field1); 
    },
    anotherHelper: function() {
        if (calcField(this.field1) > 300) 
            return calcField(this.field1) + ' is greater than 300'; 
        else
            return calcField(this.field1) + ' is smaller than 300';
    }
});

and

Alternatively is there a way to define reactive Template instance variables?

you can use Session variables or Reactive variable

Annihilation answered 29/1, 2015 at 10:19 Comment(1)
I think managing that large a number of Session variables would not be too wise - maybe a local collection would be better. With that said, I think your answer is a great option. Should have thought of it :)Clinkerbuilt
C
5

Disclaimer: This may not answer your question directly, but it might be helpful for people stuck with a similar use case:

Sometimes it's easy to get locked into the "Meteor way", that standard Javascript rules are forgotten.

Two use cases that sound similar to what you're trying to do:

1. For helpers/events that you can access anywhere on the client-side, simply set a global helper.

Put this in, say, client/helpers.js:

Helpers = {
    someFunction: function(params) {
        /* Do something here */
    }
}

Now Helpers.someFunction() is available to all templates.

If you want to bind the local template instance to it for some reason, again, it's standard JS:

var boundFunction = Helpers.someFunction.bind(this);

2. To create reusable Blaze helpers inside of templates, use Template.registerHelper

For example, this function uses the "numeral" library to format numbers:

Template.registerHelper('numeral', function(context, opt) {
    var format = (opt.hash && opt.hash.format) || '0,0.00';
    return numeral(context || 0).format(format);
});

You can use this in any template like so:

{{numeral someNumberVariable format='0,0'}}
Canice answered 3/3, 2015 at 17:12 Comment(2)
UI.registerHelper is now Template.registerHelperAve
The approach of creating Helpers = {...} didn't work for me, but Template.registerHelper( nameStr, function ) worked fine.Mauriac
E
3

I found a better solution with collection hooks:

Item =  new Mongo.Collection('Items');
Item.helpers({
    isAuthor: function(){
        return this.authorId == Meteor.userId();
    },
    color: function(){
        if(this.isAuthor())
            return 'green';
        else
            return 'red';
    }
});

I then becomes functions of this, usable in both helpers and templates.

Emmery answered 3/11, 2015 at 22:57 Comment(0)
T
1

i had something similar -- i had 2 helpers in the same template that needed access to the same function. however, that function 1) needed access to a reactive var in the template, and 2) is a filter function, so i couldn't just pass in the data of that reactive var.

i ended up defining the filter function in the templates onCreated() and stored it in a reactive var, so the helpers could access it.

Template.Foo.onCreated(function () {

    this.fooData = new ReactiveVar();

    function filterFoo(key) {
        var foo = Template.instance().fooData.get();
        // filter result is based on the key and the foo data
        return [true|false];
    }

    this.filterFoo = new ReactiveVar(filterFoo);

});

Template.Foo.helpers({
    helper1: function() {
        var filterFn = Template.instance().filterFoo.get();
        return CollectionA.getKeys().filter(filterFn);
    },
    helper2: function() {
        var filterFn = Template.instance().filterFoo.get();
        return CollectionB.getKeys().filter(filterFn);
    },

});
Tomasine answered 27/5, 2016 at 17:22 Comment(0)
S
0

Since this answer is currently missing - I wanted to add an update

In the current meteor version, you should be able to call:

var TEMPLATE_NAME = //the name of your template...
var HELPER_NAME = //the name of your helper...
Template[TEMPLATE_NAME].__helpers[' '+HELPER_NAME]

You should call it like this, if you want to make sure the helper has access to this:

var context = this;
Template[TEMPLATE_NAME].__helpers[' '+HELPER_NAME].call(context,/* args */);

But be careful - this could break in future Meteor versions.

Spontoon answered 27/6, 2015 at 17:10 Comment(0)
S
0

Adding on to Nils' answer, I have been able to access Template level helpers in events using the following code:

'click a#back': (event, instance) ->
    if instance.view.template.__helpers[' complete']() && instance.view.template.__helpers[' changed']()
        event.preventDefault()
Swinney answered 9/10, 2015 at 19:12 Comment(0)
T
0

this just came up again at work, and this time we used modules. in this case, we had a number of large, related functions that had to maintain data across calls. i wanted them outside the template file but not totally polluting the Meteor scope. so we made a module (polluting the Meteor scope 1x) and called the functions therein from the template.

lib/FooHelpers.js:

FooHelpers = (function () {
    var _foo;

    function setupFoo(value) {
        _foo = value;
    }

    function getFoo() {
        return _foo;
    }

    function incFoo() {
        _foo++;
    }

    return {
        setupFoo: setupFoo,
        getFoo: getFoo,
        incFoo: incFoo
    }
})();

FooTemplate.js:

Template.FooTemplate.helpers({
    testFoo: function() {
        FooHelpers.setupFoo(7);
        console.log(FooHelpers.getFoo());
        FooHelpers.incFoo();
        console.log(FooHelpers.getFoo());
    }
});

console output is 7, 8.

Tomasine answered 29/6, 2016 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.