Accessing parent helper in Meteor - javascript

I often find myself dividing my work into templates that still could use the same helpers.
So, say I have this template structure:
<template name="MainTemplate">
<div>{{> FirstTemplate}}</div>
<div>{{> SecondTemplate}}</div>
<div>{{> ThirdTemplate}}</div>
<div>{{> FourthTemplate}}</div>
</template>
Now each of these templates wants to use the same helper, let's call it dataHelper:
Template.MainTemplate.helpers({
dataHelper: function() {
//do some stuff
return result
}
})
Sadly, this helper can't be accessed in template first through fourth by simply typing {{dataHelper}} like how events work.
My solution has been to create a global helper instead, but that seems a tad overkill, especially since I have a few pages that don't care about these helpers at all. Another solution is to create four separate helpers but, hey, DRY.
Am I missing something simple here?

There isn't an obvious way to do this in the current version of meteor. One solution is for the child template to "inherit" the helpers from the parent. You can do this pretty easily using meteor-template-extension. Here's an example:
html
<body>
{{> parent}}
</body>
<template name="parent">
<h1>parent</h1>
{{> child}}
</template>
<template name="child">
<h2>child</h2>
<p>{{saySomething}}</p>
</template>
js
Template.parent.helpers({
saySomething: function() {
return Random.choice(['hello', 'dude!', 'i know right?']);
}
});
Template.child.inheritsHelpersFrom('parent');
The template child inherits all of its parent's helpers so it has direct access to saySomething.
This technique has two drawbacks:
you have to specify the inheritsHelpersFrom relationship
all of the parent's helpers are inherited

You can access your parent helpers using either a notation like {{yourParentHelper ..}} with two dots. Have a look here for more informations (end of the article)
You can also access parent data context in javascript like that:
var parent_data = Template.parentData();
By the way, you can add a parameter to reach the third parent, for instance:
var parent_data = Template.parentData(3);

The double dot notation seems to work best within {{#each}} loops, and I'm not having any luck within actual child templates. One option would be to use {{#with}}, although that limits you to basically one helper. e.g.:
<template name="parent">
{{#with dataHelper}}
{{> first}}
{{> second}}
{{/with}}
</template>
This will set the data context of the child helpers to dataHelper, and you can access them with {{this}} inside the template. I suppose you could make dataHelper an object and then pass in multiple pieces of data that way.

Related

Meteor data contexts - Global with respect to a single template and its descendants

In Meteor, Imagine a case where:
Session is too global.
Passing data to children templates manually like {{> child specificVar=specificVar}} is too verbose and redundant when you want specificVar to be accessible by every descendant of a template.
How can I get the in-between, where a parent creates a variable, like a ReactiveVar, and any template living within it (children, or their children, etc.) can read and write this variable?
For example:
Javascript
Template.parent.onCreated(function() {
this.specificVar = new ReactiveVar([]);
});
Template.parent.helpers({
parentHelper() {
return Template.instance().specificVar.get();
},
});
Template.child.helpers({
childHelper() {
return Template.instance().specificVar.get();
},
});
Template.grandchild.helpers({
grandchildHelper() {
return Template.instance().specificVar.get();
},
});
HTML
<template name="parent">
{{parentHelper}}
{{> child}}
</template>
<template name="child">
{{childHelper}}
{{> grandchild}}
</template>
<template name="grandchild">
{{grandchildHelper}}
</template>
This kind of data passing isn't directly addressed within vanilla Blaze, but there are a couple workarounds within the Blaze ecosystem.
Template Extensions (https://github.com/aldeed/meteor-template-extension)
This is very similar to what you wanted to do in your example. The templateInstance.get(fieldName) function allows any descendant to fetch a desired property from the ancestor. This allows your grandchild template to write e.g.
Template.grandchild.helpers({
grandchildHelper() {
return Template.instance().get('specificVar').get();
},
});
Blaze Components (https://github.com/peerlibrary/meteor-blaze-components)
This is a more involved solution that involves defining inheritance relationships between parent, child and grandchild templates.
This is more work but also more flexible and robust; The relationships you specify will be independent of DOM structure.

accessing dynamic input values in ember #each block in ember

I'm having issues accessing input values within my component. I'm trying to dynamically create value bindings in my templates and accessing in the conponent.js file using this.controller.get("pst"+id) however the result is underfined. Using Ember 2.2
{{#each post in |pst idx|}}
{{input value=(concat 'pst' idx)}}
{{/each}}
Well, it works as expected, but why would you want to do this?
Please explain what you want to archive and then we can help better.
And to be clear, a value generated with the get helper is immutable.
Why not do something like {{input value=pst}}?
If this is not an option probably you should build your array in JS and use that then in handlebars!
Define a computed property that wraps your 'post' variable in your component.js file. Iterate over that wrapper. I think this is a powerful way of generating dynamic values.
Your template:
{{#each postWrappers as postWrapper}}
{{input value=postWrapper.value}}
{{/each}}
Your component.js:
postWrappers : Ember.computed('post', function() {
//your concat code
});

How can a helper dynamically call a component by its name like {{component componentName}} helper

How can I call inside a helper the equivalent to the Handlebars's helper {{component 'componentName' model=model}} to dynamically render components based on a programmatically changed componentName?
I'm using ember-cli 1.13.8, with Ember 2.0.1.
A bit of context
I have components called cs-widget-image, cs-widget-text, cs-widget-form that expect for a model widget based on its kind attribute.
So for a widget which its kind is image, I wanna render the component cs-widget-image, but I don't think that the logic to discover the name of the correct component should be knew by the model, so I'm not considering using the helper {{component widget.componentName}} on my view.
I think that the better would be have a helper that I can use on my views like:
{{#each manyTypesWidgets as |widget|}}
{{widget-component widget.type model=widget}}
{{/each}}
On my mind, the helper widget-component would receive a widget model, and based on its attributes do a kind of "eval" and internally call the equivalent to {{component 'componentName' model=widget}}
Ex.: With widget = {id: 1, type: 'image'}
{{widget-component widget.type model=widget}}
should programmatically call the equivalent to HandleBars helper on template:
{{component 'cs-widget-image' model=widget}}
Disclaimer about a possible duplicate question
Before mark it as duplicate, I need to say that I really found some similar questions here on StackOverflow like:
[1]
[2]
[3]
[4]
[5], but all the answers are based on an elderly version of Ember that didn't work anymore on Ember 2.0.1 and ember-cli 1.13.8.
You can build a helper and put the logic of building the component name inside of it. Let's call the helper widget-name, you'd use it like this:
{{component (widget-name widget) model=widget}}
If the logic is as simple as appending the widget type to the end of cs-widget- the following should do the trick:
{{component (concat "cs-widget-" widget.type) model=widget}}
I believe your widget-component approach is more complex, because you would have to have a computed property with the logic and then bind that in a {{component call. I hope the two suggestions are helpful :)
Won't this work for you?
//JS
widgetsWithTemplates : Ember.computed.map('manyTypesWidgets', function(widget) {
widget.set('componentName', 'cs-widget-' + widget.get('type'));
})
And in the template you just call the component by componentName property:
//HBS
{{#each manyTypesWidgets as |widget|}}
{{widget.componentName model=widget}}
{{/each}}

Meteor.js using value of a helper as template

I would like to render different template based on a helper value. I will try to write and example.
...
{{#with myHelper}}
{{> this }}
{{/with}}
...
with the helper define like this for example:
...
myHelper : function(){
return MyCollection.findOne({ userId: Meteor.userId() }).personalizeTemplate;
}
...
Unfortunately in this way doesn't work. There could be any solution for that?
You need to use a dynamic template You don't even need the {{#with}}
{{> Template.dynamic template=myHelper }}
To use templates dynamically you have to use the global Template.dynamic :
{{> Template.dynamic template="my template"}}
{{> Template.dynamic template=myHelper}}
Note that myHelper must return a String which is the name of the template.
You may also provide a data context with data :
{{> Template.dynamic template=myTemplate data=someData}}
Discover Meteor wrote an article about it. It was created back in 2014 and the UI namespace used throughout the article has since been renamed Template.

Passing variables through handlebars partial

I'm currently dealing with handlebars.js in an express.js application. To keep things modular, I split all my templates in partials.
My problem: I couldn't find a way to pass variables through an partial invocation. Let's say I have a partial which looks like this:
<div id=myPartial>
<h1>Headline<h1>
<p>Lorem ipsum</p>
</div>
Let's assume I registered this partial with the name 'myPartial'. In another template I can then say something like:
<section>
{{> myPartial}}
</section>
This works fine, the partial will be rendered as expected and I'm a happy developer. But what I now need, is a way to pass different variables throught this invocation, to check within a partial for example, if a headline is given or not. Something like:
<div id=myPartial>
{{#if headline}}
<h1>{{headline}}</h1>
{{/if}}
<p>Lorem Ipsum</p>
</div>
And the invokation should look something like this:
<section>
{{> myPartial|'headline':'Headline'}}
</section>
or so.
I know, that I'm able to define all the data I need, before I render a template. But I need a way to do it like just explained. Is there a possible way?
Handlebars partials take a second parameter which becomes the context for the partial:
{{> person this}}
In versions v2.0.0 alpha and later, you can also pass a hash of named parameters:
{{> person headline='Headline'}}
You can see the tests for these scenarios: https://github.com/wycats/handlebars.js/blob/ce74c36118ffed1779889d97e6a2a1028ae61510/spec/qunit_spec.js#L456-L462
https://github.com/wycats/handlebars.js/blob/e290ec24f131f89ddf2c6aeb707a4884d41c3c6d/spec/partials.js#L26-L32
Just in case, here is what I did to get partial arguments, kind of. I’ve created a little helper that takes a partial name and a hash of parameters that will be passed to the partial:
Handlebars.registerHelper('render', function(partialId, options) {
var selector = 'script[type="text/x-handlebars-template"]#' + partialId,
source = $(selector).html(),
html = Handlebars.compile(source)(options.hash);
return new Handlebars.SafeString(html);
});
The key thing here is that Handlebars helpers accept a Ruby-like hash of arguments. In the helper code they come as part of the function’s last argument—options— in its hash member. This way you can receive the first argument—the partial name—and get the data after that.
Then, you probably want to return a Handlebars.SafeString from the helper or use “triple‑stash”—{{{— to prevent it from double escaping.
Here is a more or less complete usage scenario:
<script id="text-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="text" id="{{id}}"/>
</script>
<script id="checkbox-field" type="text/x-handlebars-template">
<label for="{{id}}">{{label}}</label>
<input type="checkbox" id="{{id}}"/>
</script>
<script id="form-template" type="text/x-handlebars-template">
<form>
<h1>{{title}}</h1>
{{ render 'text-field' label="First name" id="author-first-name" }}
{{ render 'text-field' label="Last name" id="author-last-name" }}
{{ render 'text-field' label="Email" id="author-email" }}
{{ render 'checkbox-field' label="Private?" id="private-question" }}
</form>
</script>
Hope this helps …someone. :)
This can also be done in later versions of handlebars using the key=value notation:
{{> mypartial foo='bar' }}
Allowing you to pass specific values to your partial context.
Reference: Context different for partial #182
This is very possible if you write your own helper. We are using a custom $ helper to accomplish this type of interaction (and more):
/*///////////////////////
Adds support for passing arguments to partials. Arguments are merged with
the context for rendering only (non destructive). Use `:token` syntax to
replace parts of the template path. Tokens are replace in order.
USAGE: {{$ 'path.to.partial' context=newContext foo='bar' }}
USAGE: {{$ 'path.:1.:2' replaceOne replaceTwo foo='bar' }}
///////////////////////////////*/
Handlebars.registerHelper('$', function(partial) {
var values, opts, done, value, context;
if (!partial) {
console.error('No partial name given.');
}
values = Array.prototype.slice.call(arguments, 1);
opts = values.pop();
while (!done) {
value = values.pop();
if (value) {
partial = partial.replace(/:[^\.]+/, value);
}
else {
done = true;
}
}
partial = Handlebars.partials[partial];
if (!partial) {
return '';
}
context = _.extend({}, opts.context||this, _.omit(opts, 'context', 'fn', 'inverse'));
return new Handlebars.SafeString( partial(context) );
});
Sounds like you want to do something like this:
{{> person {another: 'attribute'} }}
Yehuda already gave you a way of doing that:
{{> person this}}
But to clarify:
To give your partial its own data, just give it its own model inside the existing model, like so:
{{> person this.childContext}}
In other words, if this is the model you're giving to your template:
var model = {
some : 'attribute'
}
Then add a new object to be given to the partial:
var model = {
some : 'attribute',
childContext : {
'another' : 'attribute' // this goes to the child partial
}
}
childContext becomes the context of the partial like Yehuda said -- in that, it only sees the field another, but it doesn't see (or care about the field some). If you had id in the top level model, and repeat id again in the childContext, that'll work just fine as the partial only sees what's inside childContext.
The accepted answer works great if you just want to use a different context in your partial. However, it doesn't let you reference any of the parent context. To pass in multiple arguments, you need to write your own helper. Here's a working helper for Handlebars 2.0.0 (the other answer works for versions <2.0.0):
Handlebars.registerHelper('renderPartial', function(partialName, options) {
if (!partialName) {
console.error('No partial name given.');
return '';
}
var partial = Handlebars.partials[partialName];
if (!partial) {
console.error('Couldnt find the compiled partial: ' + partialName);
return '';
}
return new Handlebars.SafeString( partial(options.hash) );
});
Then in your template, you can do something like:
{{renderPartial 'myPartialName' foo=this bar=../bar}}
And in your partial, you'll be able to access those values as context like:
<div id={{bar.id}}>{{foo}}</div>
Yes, I was late, but I can add for Assemble users: you can use buil-in "parseJSON" helper http://assemble.io/helpers/helpers-data.html. (Discovered in https://github.com/assemble/assemble/issues/416).
Not sure if this is helpful but here's an example of Handlebars template with dynamic parameters passed to an inline RadioButtons partial and the client(browser) rendering the radio buttons in the container.
For my use it's rendered with Handlebars on the server and lets the client finish it up.
With it a forms tool can provide inline data within Handlebars without helpers.
Note : This example requires jQuery
{{#*inline "RadioButtons"}}
{{name}} Buttons<hr>
<div id="key-{{{name}}}"></div>
<script>
{{{buttons}}}.map((o)=>{
$("#key-{{name}}").append($(''
+'<button class="checkbox">'
+'<input name="{{{name}}}" type="radio" value="'+o.value+'" />'+o.text
+'</button>'
));
});
// A little test script
$("#key-{{{name}}} .checkbox").on("click",function(){
alert($("input",this).val());
});
</script>
{{/inline}}
{{>RadioButtons name="Radio" buttons='[
{value:1,text:"One"},
{value:2,text:"Two"},
{value:3,text:"Three"}]'
}}

Categories