How to give default alternatives in Handlebars - handlebars.js

After going through the docs twice, I gave up and wrote this in a template:
{{#if myVar}}{{myVar}}{{else}}{{myDefaultVar}}{{/if}}
Am I missing some Handlebars built-in feature for this, as it feels like it is going to come up a lot! (I realize I can write my own helper for it, and will do that if Handlebars has nothing built-in.)

Pending some discovery that Handlebars supports this natively, here is the helper I wrote:
Handlebars.registerHelper("P", function(){
for(var i=0;i<arguments.length;++i)if(arguments[i])return arguments[i];
});
So the long {{#if}} becomes this:
{{P myVar myDefaultVar}}
It allows multiple defaults, using whichever comes first, so I might have something like this:
Welcome {{P user.nickname user.fullName "guest"}}!
If the user has given a nickname, use that, otherwise use their full name. If they have given neither, refer to them as "guest".

Related

Detect template change in Meteor

I'm looking to make an element flash on screen when the underlying collection is updated.
It seems to me that it would make to have an equivalent of Template.my_template.rendered = function(){} which is fired every time the template is updated.
Ideally, this function would not fire the first time the template is rendered.
This is seems like such an obvious use case, am I missing something?
For Meteor < 0.8
You should use Template.myTemplate.created to do something for the very first time. From the docs Template.myTemplate.rendered is fired everytime there is a change including the first time. If you want to limit it to only the second time and changes after that (I'm guessing this is what you want), you have to write some custom logic. Currently there is no Meteor api that supports that.
For Meteor-0.8
It seems like this API underwent some backwards incompatible changes in Meteor-0.8.
There is an elegant way to achieve this as described Adaptation to the new Meteor rendered callback here. Bascially you should modify whatever function you are attaching to the variables inside your Template's javascript by calling a helper function once. For example,
var renderCount = 1;
var myHelper = function () {
console.log("rendered #" + renderCount);
renderCount++;
};
Template.myTemplate.myVariable = function () {
myHelper();
return this.name;
};
Hope this helps. There is also another alternative approach in that same repo. You might like that one.
There is no simple solution; more discussion here: https://groups.google.com/forum/#!topic/meteor-talk/iQ37mTP3hLg

Does Ember have {{itemView}} helper that can be used with {{#collection}}?

Actually, I am looking for methods that let the View be responsible for marking the first or the last item in a collection view, and I found this one How do I add a separator between elements in an {{#each}} loop except after the last element?
.But I don't want to define itemViewClass template's attrs(classNames for example) in javascript, can I just use a {{itemView}} helper or something to define a itemView template?
{{#collection contentBinding="dataList" tagName="ol" classNames="list" itemViewClass="ItemView"}}
{{#itemView classNames="item" classNamesBinding="isLastItem:last"}}
item templates
{{/itemView}}
{{/collection}}
Although, this can be solved in another ways, I just want to know if I can find a built-in support. And I do search for a long time, just can't find Ember.Handlebars.collection's document, it's not in the latest API doc.
starting with the master version of Emberjs, you may try using a custom Handlebars "bound" helper.
it would look something like this :
{{#each item in collection}}
{{#isLastElement item}}
template if last
{{else}}
template if not
{{/isLastElement}}
both templates
{{/each}}
Ember.Handlebars.registerBoundHelper('islastElement',
function(item, options) {
if(functionTellingIfThisIsLastElement(item))
return options.fn(this);
else
return options.inverse(this);
}
);
This is just another way to see things, with functionTellingIfThisIsLastElement either looking in the collection or a property in your item. To access the collection you would have to change some stuffs here, to get the good context + parameters, depending on your code.

Can Meteor Templates access Session variables directly?

In my Meteor app I find myself writing a lot of things like:
Templates.myTemplate1.isCurrentUser = function() {
return Session.get("isCurrentUser");
};
Templates.myTemplate2.isCurrentUser = function() {
return Session.get("isCurrentUser");
};
I need many different templates (I'm using handlebars) to access the same simple value stored inside Session.
Is there a way to avoid writing the same function over and over again?
Thanks
Building on #cioddi's answer, as you can pass parameters to the Handlebars helpers, you could make it a generic function so that you can easily retrieve any value dynamically, e.g.
Template.registerHelper('session',function(input){
return Session.get(input);
});
You can then call it in your template like this
{{session "isCurrentUser"}}
Note that the auth packages come with a global helper named CurrentUser that you can use to detect if the user is logged in:
{{#if currentUser}}
...
{{/if}}
As meteor is currently using handlebars as default templating engine you could just define a helper for that like:
if (Meteor.isClient) {
Template.registerHelper('isCurrentUser',function(input){
return Session.get("isCurrentUser");
});
}
you can do this in a new file e.g. called helpers.js to keep the app.js file cleaner.
Once this helper is registered you can use it in any template by inserting {{isCurrentUser}}
Just a heads up to everyone: With the release of 0.8.0, Handlebars.registerHelper has become deprecated. Using the new Blaze engine, UI.registerHelper would be the new method of accomplishing this.
Updated version of #cioddi 's code
UI.registerHelper('isCurrentUser',function(input){
return Session.get("isCurrentUser");
});
Actually now you can just use {{#if currentUser}}
It's a global included from the accounts/auth package..
http://docs.meteor.com/#template_currentuser
You'll want to check out these handlebar helpers for meteor: https://github.com/raix/Meteor-handlebar-helpers
There's a number of session helpers, one which does what you want. From the docs:
Is my session equal to 4?: {{$.Session.equals 'mySession' 4}}
You could add a isCurrentUserTemplate and include this in your other templates with
{{> isCurrentUserTemplate}}

Loop modulo condition with Handlebars.js

I am looping through objects using Handlebars. My issue is that there is no way I can easily add some code every x iterations (% 2 or % 3).
I tried the solution proposed on How can I create conditional row classes using Handlebars.js?. It doesn't work because I am using the framework called Meteor, and my "collection" is coming from a MongoDB query through Meteor. Using that solution, the "Context" is equal to the whole Meteor context and my collection is nowhere to be found...
My first idea was to create a new boolean property on my object called "eof" and a Handlebars condition would be triggered with some more code if it is equal to true... but I would like something cleaner and not dependent of the server-side.
How would you do it?
Thanks a lot!
See Tom Coleman's answer.
You could add a template helper that would look something like:
Template.tableWithDifferentColorRows.helpers({
modulo3: function() {
return (this.index % 3) === 0;
},
});
// in the html:
{{#each_with_index collection}}
<div {{#if modulo3}}style='color:purple'{{/if}}>
actual content...
</div>
{{/each}}

Can I subscribe to Meteor Session for reactive template render updates?

Is there a way to subscribe to the Meteor Session object so that a reactive template view automatically renders when data is .set on the Session object? Specifically the key/name and value data?
I have a similar question related to rendering Meteor Session object data when iterated over. This question is specifically different on purpose. I want to get an answer out on an alternate way and possibly better way to do the same thing.
I do not want to have to call Session.get('name'); This use case is because I don't know the names in the Session object.
So, I would like to be able to have something in handlebars that allows me to
Psuedo code...
{{#each Session}}
{{this.key}} {{this.value}}
{{/each}}
Unsure about subscribing to the session, but for the second part of your question, you can use this:
Template.hello.session = function () {
map = []
for (prop in Session.keys) {
map.push({key: prop, value: Session.get(prop)})
}
return map
}
then in your template:
{{#each session}}
{{key}} {{value}}
{{/each}}
Not sure if there's a more elegant way but it works.
I don't think so.
Have a look at https://github.com/meteor/meteor/blob/master/packages/session/session.js. It's actually reasonably simple. The invalidation happens in lines 45-47. You'll see calling Session.set invalidates anyone listening to that key specifically (via Session.get) or to the new or old value (via Session.equals). Nothing in there about invalidating the session as whole.
On the other hand, considering how simple it is, it wouldn't be super hard to write your own data structure that does what you want. I'm not sure what your use case is, but it might make a lot of sense to separate it from the session.
Yes you can subscribe to Session values.
Take a look a the docs for autosubscribe:
http://docs.meteor.com/#meteor_autosubscribe
// Subscribe to the chat messages in the current room. Automatically
// update the subscription whenever the current room changes.
Meteor.autosubscribe(function () {
Meteor.subscribe("chat", {room: Session.get("current-room");});
});
This code block shows one way to use this. Basically any time the value of "current-room" is changed Meteor will update the views.
EDIT
I misunderstood the initial question and decided I need to redeem myself somewhat. I just did some tests and as far as I can tell you can currently only subscribe to collection.find() and session.get() calls. So you can't subscribe to the whole session object or even to the keys object.
However, you can set a Session value to an object this may not be the most elegant solution but this worked for me on keeping track of the keys object with some hackery to keep from getting a circular object error.
var mySession = {
set: function(key, val){
var keys = Session.keys,
map = {};
Session.set(key, val);
for (var prop in keys) {
if(!(prop === "keys")){
map[prop] = keys[prop];
}
}
Session.set('keys', map);
}
};
This gives you something that looks a lot like original functionality and can help you keep track and update templates as you add or change Session values.
Here's my template helper (borrowed from previous answer):
Template.hello.keys = function() {
map = [];
keys = Session.get('keys');
for (var prop in keys) {
map.push({key:prop, value:keys[prop]});
}
return map
};
And here's the actual template:
<template name="hello">
<div class="hello">
{{#each keys}}
{{key}} {{value}} <br />
{{/each}}
</div>
</template>
This way you can just call mySession.set("thing", "another thing") and it will update on screen. I am new to Javascript so someone please let me know if I'm missing something obvious here, or let me know if there is a more elegant way of doing this.
While accessing the session object in this manner is doable .. You're not drinking the cool-aid.
In other words, Meteor excels in the respect of having publish/subscribe. Its not clear (to me) how much data you've got to put into the Session object before a browser crashes; I would rather not find out.
You would merely put all your session dependent code in a Deps.autorun() function to plug into subscriptions as mentioned in an earlier answer. Any time a session changes it'll modify the subscription; you can attach a ready callback, or use subscription.ready() checks to initiate specific actions, but ideally you'd structure your templates in a way that you could use rendered/destroyed functions, taking care to use {{isolate}}/{{constant}} when possible.
What you're getting at is kind of a difficult way to do something simple; and may not adhere to changes in meteor in the future; we can always be assured publish/subscribe will function as expected... Given Session is such a simple object.. whats to say someone designs something better shortly.. and it gets merged into meteor; and you're left with some weird workflow based on custom objects that may not break the Session; but may impact other parts of your interface.

Resources