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}}
Related
Defined 3 global helpers in Meteor as follows:
Template.registerHelper('activeTrips', function () {
console.log("Global Active Trips");
return Trips.find().count();
});
Template.registerHelper('isSuperUser', function () {
console.log("Global isSuperUser");
return Meteor.user().username === "superuser";
});
Template.registerHelpr('isAdminUser', function () {
console.log("Global isAdmin");
return Roles.userIsInRole(Meteor.user(), ['admin']);
});
Used as template count displays {{activeTrips}} displays count correctly!
But other helpers - which return True or False - do not seem to work with the handlebars {{#if}} construct?
For example {{#if isAdminUser}} do admin stuff {{/if}} never works even if I force the function to return true - in fact the helper never gets called according to my console output.
First, I would make sure that you have defined all three of your global template helpers in a Javascript file that is loaded only on the client (located within a 'client' directory).
Second, I would make sure that this Javascript file is loaded before your template code that uses these template helpers is loaded (for example, make sure that the Javascript file with the global template helpers is located within a 'lib' directory under the top-level 'client' directory, while the template code is located in a Javascript file in another directory, such as 'templates', under the same top-level 'client' directory).
Third, I would make sure that you spell Template.registerHelper properly wherever the template helper code is written (I noticed that you misspelled "registerHelper" in your code example above).
After verifying all of these things, does the code still not work as expected?
I have a simple template which has a search box in it:
<template name="search">
<input type="text" name="search" id="search" />
{{> search_results}}
</template>
and obviously, the search results template:
<template name="search_results">
<ul>
{{#each results}}
<li>{{this.resultValue}}</li>
{{/each}}
</ul>
</template>
There is an event for keyp on the search input:
Template.search.events({
'keyup input#search': function (e) {
// fetch result from db
}
});
My problem is, where i have the comment: fetch result from db how do i get the search results template to auto update with the results from the db query?
To be clear: i can get the results fine, just cant see how to get the results template to update.
Essentially, something in the template you want to rerender (search_results) has to be reactive and register a changed event. Out of the box, that means it could be any of the data sources listed here. Since this isn't happening for you, I assume that when you "fetch results from db" you're not actually returning the result of a minimongo query, as this would be reactive by default.
If so, this kind of problem is often most easily solved by rolling your own reactivity using the Deps.Dependency prototype, which (as I understand it) underpins all the other reactive data sources other than the minimongo itself.
If you set var resultsChanged = new Deps.Dependency(), you get an object with two methods: depends and changed. Invoke the former in any computation you want to rerun when it changes and the latter to register a change. Session variables are basically just key/value stores with these Deps.Dependency methods attached to their get and set methods respectively.
So if you have resultsChanged initialised, you just need to make sure your search_results template depends on it, either by adding a new helper function (if results is in the data context as opposed to being a call to a helper), or amending the results helper itself. I'll assume the latter, but adding a new helper instead would be equally trivial.
Template.search_results.helpers({
results: function() {
resultsChanged.depend();
// get your results from wherever
return results;
}
});
That will rerun every time resultsChanged changes, so you just have to add the appropriate code in your callback when you fetch the results:
'keyup input#search': function (e) {
// fetch result from db, passing "resultsChanged.changed()" in a callback
}
Obviously, if the result fetching is synchronous, you don't even need to pass a callback, you can just do it on the next line.
I hope that is vaguely helpful - please let me know if it's not. If my assumption that you're not just pulling something out of the database is wrong, then this is probably on totally the wrong track, so I would recommend posting your actual "fetch result from db" code in your question.
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".
I want to perform a Meteor collection query as soon as possible after page-load. The first thing I tried was something like this:
Games = new Meteor.Collection("games");
if (Meteor.isClient) {
Meteor.startup(function() {
console.log(Games.findOne({}));
});
}
This doesn't work, though (it prints "undefined"). The same query works a few seconds later when invoked from the JavaScript console. I assume there's some kind of lag before the database is ready. So how can I tell when this query will succeed?
Meteor version 0.5.7 (7b1bf062b9) under OSX 10.8 and Chrome 25.
You should first publish the data from the server.
if(Meteor.isServer) {
Meteor.publish('default_db_data', function(){
return Games.find({});
});
}
On the client, perform the collection queries only after the data have been loaded from the server. This can be done by using a reactive session inside the subscribe calls.
if (Meteor.isClient) {
Meteor.startup(function() {
Session.set('data_loaded', false);
});
Meteor.subscribe('default_db_data', function(){
//Set the reactive session as true to indicate that the data have been loaded
Session.set('data_loaded', true);
});
}
Now when you perform collection queries, you can check if the data is loaded or not as:
if(Session.get('data_loaded')){
Games.find({});
}
Note: Remove autopublish package, it publishes all your data by default to the client and is poor practice.
To remove it, execute $ meteor remove autopublish on every project from the root project directory.
Use DDP._allSubscriptionsReady() (Meteor 0.7)
As of Meteor 1.0.4, there is a helper that tells you exactly when a particular subscription is ready: Template.instance().subscriptionsReady().
Since this question is a duplicate, please check my answer in the original question, Displaying loader while meteor collection loads.
You can also do template level subscriptions:
Template.name.onCreated(function(){
var self = this;
this.autorun(function(){
const db = this.subscribe('publicationname', [,args]);
if(db.isReady()){
"You'll know it's ready here" .. do what you need.
}
});
})
This makes it easier to know inside the template too. you can just call
{{#if Template.subscriptionsReady}}
{{else}} Loading Screen may be
{{/if}}
You could check when a result is finally returned if you know that your Games collection is never empty:
Meteor.autorun(function() {
if(Games.findOne() && !Session.get("loaded")) {
Session.set("loaded",true);
//Its ready..
console.log(Games.findOne({}));
}
});
You can also use this in your templates:
Client js:
Template.home.isReady = function() { return Session.get("loaded") };
Html
<template name="home">
{{#if isReady}}
Yay! We're loaded!!
{{else}}
Hold an a second
{{/if}}
</template>
Here is another tidbit of information for those who may be using userid or some part of user info stored in Meteor.users database. When the page first loads the Meteor subscribe, going on in the background, may not be complete itself. Therefor when you try to connect to another database to query for that, it will not pull the information. This is because the Meteor.user() is still null itself;
The reason, like said above, is because the actual Meteor users collection has not gotten through pulling the data.
Simple way to deal with this.
Meteor.status().connected
This will return true or false, to let you know when the Meteor.user collection is ready. Then you can go on about your business.
I hope this helps someone, I was about to pull my hair out trying to figure out how to check the status. That was after figuring out the Meteor user collection itself was not loaded yet.
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.