Is there a way to pass variables into templates in Meteor?

JavascriptTemplatesMeteor

Javascript Problem Overview


I've been experimenting with Meteor and ran into something I couldn't figure out. For fun, I was trying to make a slot machine. I had the following HTML:

<div class="slot-wrapper">
  {{> slot}}
  {{> slot}}
  {{> slot}}
</div>

<template name="slot">
  <div class="slot">
    <div class="number"><span>{{ number }}</span></div>
    <div class="divider"></div>
  </div>
</template>

I want to have a different number for each slot. Is it possible to pass variables into template? Something like this:

<div class="slot-wrapper">
  {{> slot 1}}
  {{> slot 2}}
  {{> slot 3}}
</div>

<template name="slot">
  <div class="slot">
    <div class="number"><span>{{ number i}}</span></div>
    <div class="divider"></div>
  </div>
</template>

Maybe I'm thinking about this the wrong way and there's a better way.

Javascript Solutions


Solution 1 - Javascript

All of the previous answers are overkill or outdated. Here's how you can pass static parameters into templates, directly from HTML+Spacebars code, as of Meteor 0.8.x:

<div class="slot-wrapper">
  {{> slot number="1"}}
  {{> slot number="2"}}
  ...
</div>

<template name="slot">
  <div class="number"><span>{{number}}</span></div>
</template>

All you have to do is pass key="value" parameters in the {{> template}} inclusion call:

{{> slot number="1"}}

Learn more at Spacebars Secrets: Exploring Meteor Templates.


If you want to pass the caller template's data to the child/nested/called template, here's how to do it: pass nothing. Instead, from the nested template, access the parent data context, ../:

<div class="slot-wrapper">
  {{> slot number="1"}}
  {{> slot number="2"}}
  ...
</div>

<template name="slot">
  <div>Machine name: {{../name}}</div>
  <div class="number"><span>{{number}}</span></div>
</template>

Solution 2 - Javascript

Turns out there is another way.

I was trying to find out how to do this by googling various searches and found this question but nothing that suited my purpose. TomUnite's answer works unless you want to put the nested templates in different places in the parent template.

So after much searching I found 'an' answer in the meteor codebase. (Not saying it's the definitive answer but it does work)

<template name="slots">
  {{> slot one}}
  <div>..something else</div>
  {{> slot three}}
  {{> slot two}}
</template>

<template name="slot">
  <div class="slot">
    <div class="number"><span>{{number}}</span></div>
    <div class="divider"></div>
  </div>
</template>

As you see we can specify the template instances in any order. The second parameter is actually a variable that should be defined, so:

Template.slots.one = {
  number: 1
}
Template.slots.two = {
  number: 2
}
Template.slots.three = {
  number: 3
}

This could be made into more succinct code with a loop or maybe using the underscore.js function _.extend on the slots object. Also, We can pass multiple fields of data into these objects.

Solution 3 - Javascript

I wanted to leave this as a comment, because it's just a clarification on Joc's answer, but couldn't, so here it is with plus the example I worked with.

Only ONE argument can be passed to the template :

{{> singleItemInfo arg1}}

this argument must be an object such as :

{
    number: 1,
    number2: 2,
    numberComposite: {
        subnum1: 10,
        subnum2: 20
    }
};

the argument values can be accessed via their keys, and the scope can be switched to get the subitems with the

{{#with numberComposite}}

Here's the full code for the example :

<html file>

<body>
    {{ itemsView }}
</body>

<template name="itemsView">
    {{> singleItemInfo arg1}}
</template>

<template name="singleItemInfo">
    arg1 = {{ number }}
    arg2 = {{ number2 }} 
    {{#with numberComposite}}
        subArg1 = {{ subnum1 }}
        subArg2 = {{ subnum2 }}
    {{/with}}
</template>

<javascript file>

Template.itemsView.arg1 = {
    number: 1,
    number2: 2,
    numberComposite: {
        subnum1: 10,
        subnum2: 20
    }
};

OUTPUT:

arg1 = 1 arg2 = 2 subArg1 = 10 subArg2 = 20 

Solution 4 - Javascript

Better Answer:

The two solutions that are available to making a template context sensitive under the new Blaze layout are:

  1. Passing arguments to the template directly

    {{> contextSensitiveTemplate context_1='x' context_2='y' }}

  2. Using a helper in the template that understands the context. Call the helper like this:

    {{ contextHelperName ../.. .. this }}

And

Template.contextSensitiveTemplate.contextHelperName = function(parent_template, current_template, current_value_inside_each_loop) {
  return context_dependent_value_or_html     
}

Solution 5 - Javascript

This is what I have done to achieve it. I am fairly new to Meteor so there may be a better way:

Slot.html:

<head>
  <title>Slot</title>
</head>

<body>
  <div class="slot-wrapper">
    {{> slots}}
  </div>
</body>

<template name="slots">
  {{#each slots}}
    {{> slot}}
  {{/each}}
</template>

<template name="slot">
  <div class="slot">
    <div class="number"><span>{{number}}</span></div>
    <div class="divider"></div>
  </div>
</template>

Slot.js:

if (Meteor.is_client) {
  Template.slots.slots = function () {
    var returnArray = new Array();
    returnArray[0] = { 'number': 10 };
    returnArray[1] = { 'number': 87 };
    returnArray[2] = { 'number': 41 };
    return returnArray;
  };
}

if (Meteor.is_server) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}

Hope this was some help to you!

Solution 6 - Javascript

I usually use these two Handlebars helpers:

Handlebars.registerHelper('partial', function(templateName, options) {
    return new Handlebars.SafeString(Template[templateName](options.hash));
});

Handlebars.registerHelper('partialWithContext', function(templateName, context, options) {
    var extendedContext = _.extend(context, options.hash);
    return new Handlebars.SafeString(Template[templateName](context));
});

You can use it like this (suppose you have a template called menuItem):

{{partial 'menuItem' command='Open'}}

Or inside an iteration (suppose you have a template called userProfile):

{{#each regularUsers}}
    {{partialWithContext 'userProfile' . isAdmin=false}}
{{/each}}

{{#each admins}}
    {{partialWithContext 'userProfile' . isAdmin=true}}
{{/each}}

With Spacebars, you can achieve a somewhat similar behavior. In partial.js :

Template.partialWithContext.chooseTemplate = function (name) {
    return Template[name];
};

In partial.html:

<template name="partialWithContext">
	{{#with chooseTemplate name}}
		{{#with ../data}}
			{{> ..}}
		{{/with}}
	{{/with}}
</template> 

Use it like this:

{{#each commands}}
	{{> partialWithContext name="commandListItem" data=this isAdmin=false}}
{{/each}}
{{#each adminCommands}}
	{{> partialWithContext name="adminCommandListItem" data=this isAdmin=true}}
{{/each}}

Hope it will do the trick.

Solution 7 - Javascript

Use this when you pass just one argument.

<div class="slot-wrapper">
    {{> slot 1}}
    {{> slot 2}}
</div>

<template name="slot">
    <div class="slot">
        <div class="number"><span>{{this}}</span></div>
        <div class="divider"></div>
    </div>
</template>

No javascript required to do it. If you need more than a argument try Dan's way.

Solution 8 - Javascript

This information is out of date, passing arguments is described in detail for Blaze layout engine here: https://www.discovermeteor.com/blog/spacebars-secrets-exploring-meteor-new-templating-engine/

Solution 9 - Javascript

lots of good info in here. my specific situation is i also wanted to pass in some template data.

i want to make the child Blaze component re-usable, so all the data must be passed in. as an example, let's say this component shows a grade (i.e. A, B, C, etc.). on a page, i want to use the component twice: your grade, and your classmates' average grade.

here's the child component...

Grade.html

<template name="Grade">
    <h3>{{title}}</h3>
    <div>{{grade}}</h3>
</template>

title can be hardcoded in the parent, but the grade comes from the db. here's how i code the parent...

GradePage.html

<template name="GradePage">
    {{> Grade grade=gradeYours title="Your Grade" }}
    {{> Grade grade=gradeClass title="Class Grade" }}
</template>

GradePage.js (in real life it's reactive, but simplified here)

Template.GradePage.helpers({
	gradeYours: function () {
		return 'A-';
	},
	gradeClass: function () {
		return 'B+';
	}
});

that's it. the child component doesn't have to reach out at all to get its values.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJordan BrownView Question on Stackoverflow
Solution 1 - JavascriptDan DascalescuView Answer on Stackoverflow
Solution 2 - JavascriptJocView Answer on Stackoverflow
Solution 3 - JavascriptjayView Answer on Stackoverflow
Solution 4 - JavascriptJimView Answer on Stackoverflow
Solution 5 - JavascriptThomas LomasView Answer on Stackoverflow
Solution 6 - JavascriptZsoltView Answer on Stackoverflow
Solution 7 - JavascriptIsaac HanView Answer on Stackoverflow
Solution 8 - JavascriptJimView Answer on Stackoverflow
Solution 9 - JavascriptzimView Answer on Stackoverflow