Where to put helper methods used in events for multiple Meteor templates - meteor

I know that I can access methods across files by omitting var, but what is the "best practices" project directory structure for defining helper methods that are used by template events across different templates.
For example, I have:
template1.js:
Template.template1.events({
'event': function () {
helper();
}
});
template2.js:
Template.template2.events({
'event': function () {
helper();
}
});

One problem with Meteor's "share code across files with globals" approach is that when you're looking at the file where a function is used, you don't know where the function is defined. I prefer to define a single global variable in each file which needs to export variables. That variable has the same name as the file, with an initial capital (or some other naming convention which identifies it as an "export object"). Exports are stored as properties of that object. So you might create a file named globalHelpers.js:
GlobalHelpers = {};
GlobalHelpers.helper = function () {
// ...
};
And then use it in other files with GlobalHelpers.helper(). If you look at this code later, you know to look in globalHelpers.js to find the helper function.
If a file exports a single class or collection then it's okay to just use that object as the export object. So instead of things.js with Things = {}; Things.Things = new Mongo.Collection... you can just do Things = new Mongo.Collection....
You might need to place the file in a lib directory so it loads before the other files.
Don't register it with Template.registerHelper unless you want to call it directly from templates with {{ }} calls.

I suggest defining such functions in /client/scripts/globalHelpers.js
Example:
Template.registerHelper('foo',function(arg1,arg2){
return arg1 + arg2;
});
Then you can use this function from anywhere by prefixing it with Blaze._globalHelpers.:
var result = Blaze._globalHelpers.foo(param1, param2);

Related

Meteor: How to access Template's Instance key in Blaze directly (without helper)?

Template.Demo.onCreated(function() {
this.test = 'Text';
});
How to access that template's instance test key in Blaze directly (without creating a helper function)?
{{Template.instance.test}} seems not to work (it was suggested here).
I don't believe this is possible with current versions of Blaze. That being said, you can simulate this by declaring a single global Template helper function, like:
Template.registerHelper('instance', function () {
return Template.instance();
});
Just define this once in a common client location, and you can then reference instance in any of your Template's. So you could reference your test variable like:
{{instance.test}}

Setting up global variable in SPA (Hot Towel)

I want to have a global variable that is accessible to modify in any view in my application. I am wondering what this the best way to do so when using hot towel. Currently, I am declareing this variable in my shell.js and referencing this in subsequent views.
My shell.js returns the global variable. My other views have the shell defined at the top like define(['viewmodels/shell'], function(shell){} and I access the shell variable like shell.variablename. I am wondering if this is the best way of handling variables that need to be accessed by more than one view.
It can be done in many ways , either u create a config.js or any file or u can include the following code inside 'vm' object.
// config.js
define(function () {
var variableName= 'BlahBlah';
return {
debugEnabled: ko.observable(true),
variableName :variableName
};
});
// include this config.js in any javascript file
define(['folderName/config'],
function (config) {
var x= config.variableName;
}
This will work

Meteor: store object in rendered() to be used in child helpers

I would like to store an object that I create during Template.myTemplate.rendered = function ( ) { ... } and use that object in Template.myChildTemplate.helpers(helpers). So far I'm resorting to using a global object, but that feels very hacky. Is there a nice Template-centric way of doing this?
You could use UI._templateInstance(); and ReactiveDict (to make your data reactive too) (added with meteor add reactive-dict
Template.myTemplate.created = function() {
this.templatedata = new ReactiveDict();
}
Template.myTemplate.rendered = function() {
this.templatedata.set("myname", "value";
};
Template.myTemplate.helpers({
myvalue: function() {
var tmpl = UI._templateInstance();
return tmpl.templatedata.get('myname');
}
});
This will allow you to use this template multiple times on the page, and still have a variable scope to each template, which global variables or non instance variables wouldn't allow.
A note of warning, the current iron router (0.7.1) breaks UI._templateInstance();, which is an open bug at the moment.
There's no such method yet, unfortunately.
The common pattern is to use reactive dict either as a file-wide variable, or in a namespace related to the template if you need the access in several files. The downside of this solution is that such variable is shared among all instances of the same template, so you have to work around this if you render this template in more than one place.
var data = new ReactiveDict();
Template.myTemplate.rendered = function() {
data.set('key', 'value');
};

How to use URL parameters using Meteorjs

How can I use URL parameters with meteor.
The URL could look like this: http://my-meteor.example.com:3000?task_name=abcd1234
I want to use the 'task_name' (abcd1234) in the mongodb query in the meteor app.
eg.
Template.task_app.tasks = function () {
return Tasks.find({task_name: task_name});
};
Thanks.
You are probably going to want to use a router to take care of paths and rendering certain templates for different paths. The iron-router package is the best one available for that. If you aren't using it already I would highly recommend it.
Once you are using iron-router, getting the query strings and url parameters is made very simple. You can see the section of the documentation here: https://github.com/iron-meteor/iron-router/blob/devel/Guide.md#route-parameters
For the example you gave the route would look something like this:
Router.map(function () {
this.route('home', {
path: '/',
template: 'task_app'
data: function () {
// the data function is an example where this.params is available
// we can access params using this.params
// see the below paths that would match this route
var params = this.params;
// we can access query string params using this.params.query
var queryStringParams = this.params.query;
// query params are added to the 'query' object on this.params.
// given a browser path of: '/?task_name=abcd1234
// this.params.query.task_name => 'abcd1234'
return Tasks.findOne({task_name: this.params.query.task_name});
}
});
});
This would create a route which would render the 'task_app' template with a data context of the first task which matches the task name.
You can also access the url parameters and other route information from template helpers or other functions using Router.current() to get the current route. So for example in a helper you might use Router.current().params.query.task_name to get the current task name. Router.current() is a reactive elements so if it is used within the reactive computation the computation will re-run when any changes are made to the route.

How to work with async code in Mongoose virtual properties?

I'm trying to work with associating documents in different collections (not embedded documents) and while there is an issue for that in Mongooose, I'm trying to work around it now by lazy loading the associated document with a virtual property as documented on the Mongoose website.
The problem is that the getter for a virtual takes a function as an argument and uses the return value for the virtual property. This is great when the virtual doesn't require any async calls to calculate it's value, but doesn't work when I need to make an async call to load the other document. Here's the sample code I'm working with:
TransactionSchema.virtual('notebook')
.get( function() { // <-- the return value of this function is used as the property value
Notebook.findById(this.notebookId, function(err, notebook) {
return notebook; // I can't use this value, since the outer function returns before we get to this code
})
// undefined is returned here as the properties value
});
This doesn't work since the function returns before the async call is finished. Is there a way I could use a flow control library to make this work, or could I modify the first function so that I pass the findById call to the getter instead of an anonymous function?
You can define a virtual method, for which you can define a callback.
Using your example:
TransactionSchema.method('getNotebook', function(cb) {
Notebook.findById(this.notebookId, function(err, notebook) {
cb(notebook);
})
});
And while the sole commenter appears to be one of those pedantic types, you also should not be afraid of embedding documents. Its one of mongos strong points from what I understand.
One uses the above code like so:
instance.getNotebook(function(nootebook){
// hey man, I have my notebook and stuff
});
While this addresses the broader problem rather than the specific question, I still thought it was worth submitting:
You can easily load an associated document from another collection (having a nearly identical result as defining a virtual) by using Mongoose's query populate function. Using the above example, this requires specifying the ref of the ObjectID in the Transaction schema (to point to the Notebook collection), then calling populate(NotebookId) while constructing the query. The linked Mongoose documentation addresses this pretty thoroughly.
I'm not familiar with Mongoose's history, but I'm guessing populate did not exist when these earlier answers were submitted.
Josh's approach works great for single document look-ups, but my situation was a little more complex. I needed to do a look-up on a nested property for an entire array of objects. For example, my model looked more like this:
var TransactionSchema = new Schema({
...
, notebooks: {type: [Notebook]}
});
var NotebookSchema = new Schema({
...
, authorName: String // this should not necessarily persist to db because it may get stale
, authorId: String
});
var AuthorSchema = new Schema({
firstName: String
, lastName: String
});
Then, in my application code (I'm using Express), when I get a Transaction, I want all of the notebooks with author last name's:
...
TransactionSchema.findById(someTransactionId, function(err, trans) {
...
if (trans) {
var authorIds = trans.notebooks.map(function(tx) {
return notebook.authorId;
});
Author.find({_id: {$in: authorIds}, [], function(err2, authors) {
for (var a in authors) {
for (var n in trans.notebooks {
if (authors[a].id == trans.notebooks[n].authorId) {
trans.notebooks[n].authorLastName = authors[a].lastName;
break;
}
}
}
...
});
This seems wildly inefficient and hacky, but I could not figure out another way to accomplish this. Lastly, I am new to node.js, mongoose, and stackoverflow so forgive me if this is not the most appropriate place to extend this discussion. It's just that Josh's solution was the most helpful in my eventual "solution."
As this is an old question, I figured it might use an update.
To achieve asynchronous virtual fields, you can use mongoose-fill, as stated in mongoose's github issue: https://github.com/Automattic/mongoose/issues/1894

Resources