Is there a way to do a conditional "and" or "or" in a Meteor template? What I am trying to do is something like this:
{{#if isInRole 'ADMIN' || isInRole 'INSPECTOR'}}
...do some stuff
{{/if}}
using helpers provided by a 3rd party package alanning:roles. This seems to have come up multiple times in my coding with Meteor and I can and have worked around it, usually by duplication of the block code, but it would be really nice if there already existed some way to handle an OR or AND condition without crazy gyrations.
Meteor using spacebar which is based on handlebars. In handlebars, there are now direct way for logical operators.
You can do small workaround to handle the or by creating a Template.helper
Template.registerHelper("equals_or", function(param, arr) {
arr = arr.split(",");
if (arr.indexOf(param) !== -1) {
return true;
}
else {
return false;
}
});
Then in the HTML you can do
{{#if equals_or isRole "ADMIN,INSPECTOR"}}
...do some stuff
{{else}}
...do some other stuff
{{/if}}
I might not be a perfect solution, but it is doing the job.
Check this answer too.
Related
I've read through the (somewhat sparse) documentation on Dynamic Templates but am still having trouble displaying dynamic content on a user dashboard based on a particular field.
My Meteor.users collection includes a status field and I want to return different content based on this status.
So, for example , if the user has a status of ‘current’, they would see the 'currentUser' template.
I’ve been using a dynamic template helper (but have also considered using template helper arguments which may still be the way to go) but it isn’t showing a different template for users with different statuses.
{{> Template.dynamic template=userStatus}}
And the helper returns a string to align with the required template as required
userStatus: function () {
if (Meteor.users.find({_id:Meteor.userId(), status: 'active'})){
return 'isCurrent'
}
else if (Meteor.users.find({_id:Meteor.userId(), status: ‘isIdle'})) {
return 'isIdle'
} else {
return ‘genericContent'
}
}
There may be much better ways to go about this but it seems a pretty common use case.
The few examples I've seen use Sessions or a click event but I’d rather use the cursor if possible. Does this mean what I’m missing is the re-computation to make it properly reactive? Or something else incredibly obvious that I’ve overlooked.
There is a shortcut for getting the current user object, Meteor.user(). I suggest you get this object and then check the value of the status.
userStatus: function () {
if(Meteor.user()) {
if (Meteor.user().status === 'active') {
return 'currentUserTemplate'; // this should be the template name
} else if (Meteor.user().status === 'isIdle') {
return 'idleUserTemplate'; // this should be the template name
}
} else {
return ‘notLoggedInTemplate'; // this should be the template name
}
}
Ended up using this approach discussed on the Meteor forums which seems a bit cleaner.
{{> Template.dynamic template=getTemplateName}}
And the helper then becomes:
getTemplateName: function() {
return "statusTemplate" + Meteor.user().status;
},
Which means you can then use template names based on the status:
<template name="statusTemplateActive">
Content for active users
</template>
(though keep in mind that Template helpers don't like hyphens and the data context needs to be set correctly)
everybody :) For example I have something like this:
{{> someCoolHelper
someParam1='someVal1'
someParam2='someVal2'
someParam3='SomeVal3'
someParam4=someAnotherHelper 'value param to this helper'
}}
So I have a function, that computes some value depending on parameter that can be passed in it. So how can I do this?
as you may read in a forum thread at meteor forum this is not possible by now in spacebars.
We began with a new package for chaining methods and arguments but its not released yet. What you could do is to use the WITH element like
Instead something like:
{{> someCoolHelper
someParam1='someVal1'
someParam2='someVal2'
someParam3='SomeVal3'
someParam4=someAnotherHelper 'value param to this helper'
}}
can be managed by:
{{#with someAnotherHelper 'value param to this helper' }}
{{> someCoolHelper
someParam1='someVal1'
someParam2='someVal2'
someParam3='SomeVal3'
someParam4=this
}}
{{/with}}
I don't like it, but sometimes necessary
Tom
P.S.: Or drop Spacebars and use React - you won't have such limitations there.
The placeholder solution until template sub expressions are available in Spacebars is to create an helper returning the adequate value in JS :
HTML
{{> myTemplate param1="A" param2=param2}}
JS
function someOtherHelper(param){
console.log(param);
}
Template.registerHelper("someOtherHelper", someOtherHelper);
Template.myTemplate.helpers({
param2: function(){
return someOtherHelper("B");
}
});
You can pass the output of a helper to another helper using parentheses. Pretty sure this is what you're after:
http://blazejs.org/guide/spacebars.html#Calling-helpers-with-arguments
You can also pass the output of a helper to a template inclusion or other helper. To do so, use parentheses to show precedence:
{{> Todos_item (todoArgs todo)}}
Here the todo is passed as argument to the todoArgs helper, then the output is passed into the Todos_item template.
Something I did in a recent project uses this functionality to allow conditional rendering of CSS classes to a component (stripped down for brevity):
# global helper to provide ternary operator
Template.registerHelper('ternary', (condition, resultTrue, resultFalse) => {
return condition ? resultTrue : resultFalse
})
# in my-template.js:
Template.My_template.helpers({
isComplete(score) {
return score === 1
}
})
# in my-template.html:
{{> some_component class=(ternary (isComplete this.score) "green" "red")}}
I have this html-File in Meteor
{{#if thevalue}}
{{> one}}
{{else}}
{{> two}}
{{/if}}
and this helper
'thevalue': Session.get('thevalue') //returns true or false
My problem is that when the Session-Value changes, the if/else-Bracktes from Spacebars do not change with it. I thought Session-Values are reactive...but maybe I have some sort of misconception how this works.
Try to write your helper as a function like so
'thevalue': function () {
return Session.get('thevalue');
}
See the docs here for more.
Session is reactive and helper is a reactive computation. The problem may be the format of your helper which should be like this:
thevalue: function(){
return Session.get('thevalue');
}
The problem could simply be that you are putting 'thevalue' in quotes and turning it in to a string where I believe it needs to run as a function.
Keep in mind if your 'thevalue' is 0 then your spacebars will return {{> two}}.
I am wondering how to solve this problem:
I have a template which contains some text with some template helpers inside:
<template>Hello {{who}}, the wheather is {{weather}}</template>
Now I need to change the content of the template dynamically at runtime, while maintaining the helper functionality. For example I would need it like this:
<template>Oh, the {{weather}}. Good evening {{who}}</template>
The text changes and the helpers are needed at different positions. Think of an application where users can create custom forms with placeholders for certain variables like the name of the user who fills out the form. Basically, the content of the template is stored in a mongo document and needs to be turned into a template at runtime, or an existing template needs to be changed.
How to approach this? Can I change the contents of a template at runtime?
To solve this use case you need to use two techniques.
Firstly you need to be able to change the template reactivel. To do this you can use Template.dynamic. eg:
{{> Template.dynamic template=helperToReturnName [data=data] }}
See here: http://docs.meteor.com/#/full/template_dynamic
Now that you can change template, you need to be able to create new templates on the fly from you database content. This is non trivial, but it's possible if you're willing to write code to create them, like this:
Template.__define__("postList", (function() {
var view = this;
return [
HTML.Raw("<h1>Post List</h1>\n "),
HTML.UL("\n ", Blaze.Each(function() {
return Spacebars.call(view.lookup("posts"));
},
function() {
return [ "\n ", HTML.LI(Blaze.View(function() {
return Spacebars.mustache(view.lookup("title"));
})), "\n " ];
}), "\n ")
];
}));
That code snippet was taken from this article on Meteorhacks, and the article itself goes into far more detail. After reading the article you'll be armed with the knowledge you need to complete the task...
Just have a helper dynamically build the entire string (remembering that this refers to the current data context):
Template.foo.helpers({
dynamicString: function(switch){
if ( switch == 1) return "Hello "+this.who+", the wheather is "+this.weather;
else return "Oh, the "+this.weather+". Good evening "+this.who;
}
});
Then in your template:
<template name="foo">
{{dynamicString}}
</template>
Alternatively, just use {{#if variable}} or {{#unless variable}} blocks to change the logic in your template. Much, much simpler.
<template name="foo">
{{#if case1}}
Hello {{who}}, the wheather is {{weather}}
{{else}}
Oh, the {{weather}}. Good evening {{who}}
{{/if}}
</template>
You can always have a template helper that computes the necessary boolean variables (e.g. case1).
I'm definitely missing something about the way Handlebars works. I need to call different partials depending on the value of a variable. Currently the only way I've found to do it is this:
<template name="base">
{{#if a}}{{> a}}{{/if}}
{{#if b}}{{> b}}{{/if}}
{{#if c}}{{> c}}{{/if}}
</template>
And in the corresponding JS:
Template.base.a = function () {
return (mode === "a");
}
Template.base.b = function () {
return (mode === "b");
}
Template.base.c = function () {
return (mode === "c");
}
...which strikes me as extremely verbose. What I'd really like to do is something like:
<template name="base">
{{> {{mode}} }}
</template>
In other words, the value of mode would be the name of the partial that is called.
This seems like it must be a very common use-case, but I can't find any examples of this online. Where have I gone wrong?
The partials are stored in Handlebars.partials so you can access them by hand in your own helper. There are a few tricky bits here though:
The contents of Handlebars.partials can be strings or functions so you have to compile the partials on first use.
Handlebars doesn't know if the partial will be text/plain or text/html so you'll need to call your helper in {{{...}}} or {{...}} as appropriate.
This stuff isn't exactly documented (at least not anywhere that I can find) so you have to reverse engineer the Handlebars source and fumble about with console.log(arguments) to figure out how to use Handlebars.partials.
You have to pass this by hand when you call the helper.
Fear not, it isn't really that complicated. Something simple like this:
Handlebars.registerHelper('partial', function(name, ctx, hash) {
var ps = Handlebars.partials;
if(typeof ps[name] !== 'function')
ps[name] = Handlebars.compile(ps[name]);
return ps[name](ctx, hash);
});
should do the trick. Then you can say:
{{{partial mode this}}}
and get on with more interesting things.
Demo: http://jsfiddle.net/ambiguous/YwNJ3/2/
Update for 2016: Version 3 of handlebars added Dynamic Partials. From the docs:
It's possible to dynamically select the partial to be executed by using sub expression syntax.
{{> (whichPartial) }}
Will evaluate whichPartial and then render the partial whose name is returned by this function.
Subexpressions do not resolve variables, so whichPartial must be a function. If a simple variable has the partial name, it's possible to resolve it via the lookup helper.
{{> (lookup . 'myVariable') }}