Clarity on template helpers


#1

This is an extension of a previous question (thank you @shock) …

…but seemed different enough to warrant a new post.

I am struggling with writing some template helpers - I’m unclear of whether I need to register helpers or not. I’ve read through the Spacebars readme but not getting it.

This is my current code, which all works properly:

<template name="Chart">
    {{target.companyName}}
    {{referenceValue}
</template>

Chart schema

{
    _id:	
    targetId:
    chartMetric:
}

Companies schema

{
    _id:
    companyName: “Name”,
    metric: {
	revenue: “100”,
	ebitda: “20”,
	netIncome: “10”
	}
}

Template.Chart.helpers({
    target: function () {
        var selection = Charts.findOne({_id: this._id}).targetId;
        return Companies.findOne({_id: selection});
    },
    referenceValue: function() {
        var selection = Charts.findOne({_id: this._id}).targetId;
        var target = Companies.findOne({_id: selection});
        var metric = Chart.findOne({_id: this._id }).chartMetric;
        switch (metric) {
            case "revenue":
                return target.revenue;
                break;
            case "ebitda":
                return target.ebitda;
                break;
            case "netIncome":
                return target.netIncome;
                break;
           default:
                return 0
       }
    }
}),

My questions are:

  • Reusing helpers within the same template

How can I reuse target in referenceValue (as well as reuse referenceValue for use in future helpers) without needing to rewrite it as I have done here? I have a number of switches and calculations to write and I know I shouldn’t be repeating it. I tried adding the following to clients/lib/helpers.js to try to register the helper then get no results:

Template.registerHelper('target', function () {
    var selection = Charts.findOne({_id: this._id}).targetId;
    return Companies.findOne({_id: selection});
    }
});
  • Dynamic spacebars path reference

Is there any way to just make the spacebars path dynamic using the field name itself and avoiding writing a switch? For instance, {{target.metric}} where the value would change when the user selects a different metric?

Thank you all in advance.


#2
<template name="Chart">
  {{#with chart=getChart}}
    {{#with chart=chart company=getCompany}}
      {{company.companyName}}
      {{referenceValue}
    {{/with}}
  {{/with}}
</template>
Template.Chart.helpers({

  getChart: function () {
    return Charts.findOne(
      this._id, 
      { fields: { targetId: 1, chartMetric: 1 }}
    );
  },

  getCompany: function () {
    return Companies.findOne(this.chart.targetId);
  },

  referenceValue: function() {
    switch (this.chart.chartMetric) {
      case "revenue":   return this.company.revenue;
      case "ebitda":    return this.company.ebitda;
      case "netIncome": return this.company.netIncome;
     }
  }

}),

#3

Thanks @Steve. I had included a very pared down version of my code for the example, so will need to take some time to see it this works in my file.

In your example above, will this allow referenceValue to be used in the next helper? This part looks very similar to what I had originally, but, for instance, if I tried to multiply the result by another number, I needed to duplicate everything, rather than just writing a helper with referenceValue * anotherNumber.


#4

If you want to reuse referenceValue:

var referenceValue = function(chartMetric, company) {
    switch (chartMetric) {
      case "revenue":   return company.revenue;
      case "ebitda":    return company.ebitda;
      case "netIncome": return company.netIncome;
     }

Template.Chart.helpers({

  referenceValue: function() {
    return referenceValue(this.chart.chartMetric, this.company);
    },

  referenceValue2: function() {
    return referenceValue(this.chart.chartMetric, this.company) / 333;
    },

  }

}),

If you want to multiply:

Template.registerHelper('mul', function(a, b) { return a * b; });
<template name="Chart">
  {{#with chart=getChart}}
    {{#with chart=chart company=getCompany}}
      {{company.companyName}}
      {{mul referenceValue 33}
    {{/with}}
  {{/with}}
</template>

Once you understand all this, you might be interested in this package: collection-helpers.


#5

@Steve, I am slowly working through your suggestions above (I’m really new at this, so taking me a long time). I have a question to clarify your use of #with. Unfortunately, the code I pasted above was probably pared down too much. If you don’t mind, I pasted a more thorough template structure below - can you tell me how this loop would change your suggestion? My biggest confusion is always “where does the code go?” Should the #with lines go outside the Table template or does it matter?.

<template name="Chart">
    <div>
        {{chartMetric}}
        {{chartPeriod}}
    </div>
    <div>
        {{>Table}}
    </div
</template>

<template name="Table">
   <div>
        <table>
            <thead>
                <tr>
                    <th>Ticker</th>
                    <th>Company</th>
                    <th>{{headingNum}}</th>
                    <th>{{headingDen}}</th>
                    <th>{{headingNum}}/{{headingDen}}</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                {{#each selections}}
                        <tr>
                            <td>{{ticker}}</td>
                            <td>{{companyName}}</td>
                            <td>${{valuationNum}}</td>
                            <td>${{valuationDen}}</td>
                            <td>{{valuationMultiple}}x</td>
                        </tr>
                {{/each}}
            </tbody>
        </table>
    </div>
</template>

valuationNum and valuationDen are both dependent on chartMetric and chartPeriod (using switches), which is what led to my difficulty. I can’t seem to access those variables in the helpers using this to find the chart, since at that point, this refers to a different document (it works headingNum and headingDen, which are outside the loop.

I hope this is clear enough and Meteor-related enough. Thank you.


#6

There are 2 ways to access the parent data context:

  • in the template html, using #with and/or ../ (see this article).
  • directly in the template helpers (see this and this)

#7

You just got me back on track with Template.parentData(n). Thank you!