63

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.

Jordan Brown
  • 13,603
  • 6
  • 30
  • 29
  • 1
    **Meteor has changed and introduced Spacebars.** See here for details: https://www.discovermeteor.com/blog/spacebars-secrets-exploring-meteor-new-templating-engine/ and Dascalescu answer: http://stackoverflow.com/a/25090098/220060 – nalply Aug 02 '14 at 13:26

9 Answers9

94

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>
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
14

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.

Joc
  • 1,059
  • 8
  • 8
9

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 
rishat
  • 8,206
  • 4
  • 44
  • 69
jay
  • 507
  • 4
  • 16
  • 2
    As a complement, you can use reactive data-sources in there, with something like `Template.itemsView.arg1 = function(){ Session.get('arg1object') }`, the subtemplate will be automatically updated with the data-source. – ejoubaud Jul 22 '13 at 22:16
  • @EmmanuelJoubaud I passed data from a helper into a template like this which should be a reactive data source but the template the data was passed to is not updating when the helper updates. Any ideas? – Adam Moisa Jul 30 '17 at 13:33
  • Figured it out, I getting the data from the template in my helper (so i can manipulate it further) from Template.instance(). and then returning it to the template. I changed it to get the data from Template.instance().data which now works. – Adam Moisa Jul 30 '17 at 13:46
5

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     
}
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
Jim
  • 521
  • 8
  • 14
3

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!

Thomas Lomas
  • 1,553
  • 10
  • 22
2

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.

Zsolt
  • 3,263
  • 3
  • 33
  • 48
  • 1
    This no longer works with the introduction of Spacebars. I'm still attempting to work out a solution. – Seiyria Jul 19 '14 at 17:17
  • 1
    Hi Seiyria. Yes, you cannot use the original solution because they are not using Handlebars anymore. You can find my updated approach in the answer. – Zsolt Jul 22 '14 at 07:23
2

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.

Isaac Han
  • 348
  • 3
  • 8
0

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/

Jim
  • 521
  • 8
  • 14
  • 2
    You should write a summary about the changes in Blaze and then directly answer the question. Then you don't get downvoted. (I didn't downvote your answer.) – nalply Aug 02 '14 at 13:28
0

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}}</div>
</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+';
    }
});

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

Omar Chakroun
  • 119
  • 1
  • 2
  • 12
zim
  • 2,386
  • 2
  • 14
  • 13