Display in meteorjs page a json object value with numeric name - meteor

I would like to display the value 10 of this object {'1':10}, but I cannot find the right syntax for the template.
Client js code is:
Template.hello.data = function () {
return { '1' : 10};
};
Client html code is:
<body>
{{> hello}}
</body>
<template name="hello">
<p>{{data.1}}</p>
</template>
This gives the error:
hello.html:6: Expected IDENTIFIER
<p>{{data.1}}</p>
Of course changing to something like { 'A1' : 10} it works, but I would like to keep the field name '1'.

Looks like the data parser is treating the 1 as an integer rather than a string. Try bracket notation (see answer at: https://stackoverflow.com/a/12272511/1327678)
{{data.[1]}}

Related

Write custom iterator for Spacebars

I'm trying to write a custom iterator in spacebars (I'm using meteor 1.1.3). The iterator is to be a sequential for loop (basically to replace my usage of #each when needed since I believe #each is not guaranteed to be sequential in its iteration).
I have tried the following:
In lib -
UI.registerHelper 'sequentialFor', () ->
ret = ""
for i in [0...#.length]
id = #[i]
ret = ret + Template.noop
ret
noop.html -
<template name="noop">
{{> UI.contentBlock this}}
<template>
main.html -
{{#sequentialFor ids}}
<div id="wow-{{this}}">stuff</div>
{{/sequentialFor}}
ids in the above is an array of strings passed from one of main's template helpers.
Right now it complains the the return from my UI helper is [object Object] [object Object].
For sanity's sake I know that if I replace my UI helper with:
UI.registerHelper 'sequentialFor', () ->
//ret = ""
//for i in [0...#.length]
// id = #[i]
// ret = ret + template
id = #[0]
Template.noop
I get that the div in my main.html shows up with the appropriate id as a part of its id attribute as desired. However, I can't seem to make the for loop work.
I can't simply return the div in main.html directly from the helper because I have a lot of divs that I need to wrap with my new iterator, each of which has very different attributes.
I guess the simple question is, how do I define my own block iterator (akin to #each) in spacebars?
The more difficult question may be, what is wrong with my approach above?
I have considered a wide array of resources but have only the found the following to be very helpful:
How to pass an object from to a block helper back to the block in meteor blaze?
https://github.com/meteor/meteor/wiki/Using-Blaze
https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md
Iterating over basic “for” loop using Handlebars.js
NOTE I'm using coffeescript
I managed to get a custom iterator using a recursive technique similar to what you might use in Haskell or Lisp:
<body>
{{#countdown n=5}}
<p>item {{this}}</p>
{{/countdown}}
</body>
<template name="countdown">
{{#if positive}}
{{> Template.contentBlock n}}
{{#countdown n=nMinusOne}}
{{> Template.contentBlock this}}
{{/countdown}}
{{/if}}
</template>
Template.countdown.helpers({
positive: function () {return this.n > 0;},
nMinusOne: function () {return this.n - 1;}
});
See meteorpad.
The performance is probably far worse than the usual {{#each}}.
It appears to me that you want to create a <div> for each of an array of IDs (correct me if I'm wrong). This is how I would go about it, no custom iterator necessary:
Template.registerHelper('ids', function(arrayWithIds) {
if (!arrayWithIds) return [];
// do some sorting or whatever with arrayWithIds, for example:
var arrayOfIds = _.map(arrayWithIds, function(obj) {
return obj._id;
});
return arrayOfIds;
});
Then in main.html:
{{#each ids someDataSetWithIds}}
// `someDataSetWithIds` is the helper's parameter
// `this` in each case is an ID
<div id="wow-{{this}}"></div>
{{/each}}
If your helper returns an object, you would use this._id in the template, instead. Did I misunderstand what you're trying to achieve?

Template is re-rendered even though there is no data change

I'm struggling with an issue that I will explain giving a simple demo.
There's following very simple document in People Collection.
{
"_id" : "vxmLRndHgNocZouJg",
"fname" : "John" ,
"nicks" : [ "Johnny" , "Jo"]
}
Now let's consider following templates. Basically I display username and a list of nicknames with input field for adding more nicknames.
<head>
<title>test</title>
</head>
<body>
{{> name}}<br/>
{{> nicks}}
</body>
<template name="name">
<input type="text" value="{{fname}}"/>
</template>
<template name="nicks">
{{#each nicks}}
<div>{{this}}</div>
{{else}}
no nicks yet
{{/each}}
<input type="text" name="nicks"/>
<input type="submit"/>
</template>
My client javascript code is as follows:
Template.name.fname = function() {
return People.findOne({"fname" : "John"},{
transform : function(doc) {
return doc.fname;
}
});
}
Template.name.rendered = function() {
console.log('Template "name" rendered!');
}
Template.nicks.nicks = function() {
var john = People.findOne({"fname" : "John"});
if(john) return john.nicks;
}
Template.nicks.events({
'click input[type="submit"]' : function () {
var johnId = People.findOne({"fname" : "John"})._id; // demo code
People.update(johnId,{
$addToSet : {
nicks : $('input[name="nicks"]').val()
}
})
}
});
My problem is that after adding nickname (update of nicks field in a document) template name is re-rendered (I know because I console.log it). When I query People collection to provide data for name template I use transform option so changes in nicks field shouldn't have impact on name template.
Meteor docs supports this:
Cursors are a reactive data source. The first time you retrieve a cursor's documents with fetch, map, or forEach inside a reactive computation (eg, a template or autorun), Meteor will register a dependency on the underlying data. Any change to the collection that changes the documents in a cursor will trigger a recomputation.
Why template name is re-rendered then?
The template is re-rendered because you change the People collection.
When you alter the People collection, Meteor automatically assumes that everything that it provides data to needs to be recalculated. (Which your name template does via Template.name.fname.
Even though you transform the output of the cursor, the People collection has changed in some way. The query is done before the transform is used, in other words, its not the transform that is looked at but the whole collection.
Meteor thinks that perhaps your document with {'fname':'John'} may have some other field that might have changed and it needs to requery it to check (which the nicks field has been altered). The transform is then applied after the requery.
Your HTML might not actually change at this point, only if the cursor returns something different will the html be changed.
If it becomes an issue in any scenario (i.e forms being cleared or DOM being changed when it shouldn't be) you can use the {{#isolate}} {{/isolate}} blocks to ensure that only everything inside them is re-rendered and nothing outside.

Using variables for a partial template

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') }}

Meteor: Passing more than one value to a template

I want to create a template/js combo similar to the ones below. What I would like is to have two variables available to the 'collection' template
<template name="collection">
Title: {{title}}
<UL>
{{#each items}}
{{> item}}
{{/each}}
</UL>
</template>
<template name="collection_items">
<LI>{{item_title}}</LI>
</template>
Where the javascript function would be something like:
Template.collection.data = function() {
var record = Record.findOne({whatever:value});
return { title: record.title, items: record.items }
}
I've tried using Handlebars' {{#with data}} helper and return an object as above, but that just crashed the template. I've tried creating a 'top level' function like:
Template.collection = function () {... }
but that also crashed the template.
What I'm trying to avoid is having two separate functions (one Template.collection.title, and one Template collection.items) where each of them calls a findOne on the Record collection where really its the same template and one call should suffice.
Any ideas?
Template.collection = function () {... }
Template.collection is not a function, it's an instance and thus an object.
You can type Template.collection in the console to see something essential as well as Template.collection. and autocomplete that to see its methods and fields.
For a #with example, the Todos indeed doen't seem to contain one as you have outlined in your comments. So, an example use of it can be found here:
https://github.com/meteor/meteor/blob/master/packages/templating/templating_tests.js#L75
https://github.com/meteor/meteor/blob/master/packages/templating/templating_tests.html#L92
Here is another example that I tried that works on both the current master and devel branch:
<head>
<title>test</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
{{#with author}}
<h2>By {{firstName}} {{lastName}}</h2>
{{/with}}
</template>
And the JS part of it:
if (Meteor.is_client) {
Template.hello.author = function () {
return {
firstName: "Charles",
lastName: "Jolley"
};
};
}
Any specific reason why you're hoping to avoid two functions?
From your code sample I see one issue: the first template is calling a second template with this line:
{{> item}}
But your second template is not called 'items'. I believe that your second template should be called this way:
<template name="item">
Seems that it would be simple enough to have helper functions for the first and the second. Although I haven't gotten it to work with my own code, I believe the second helper function would want to use the 'this' convention to refer to the collection you're referring to.
Cheers - holling
Tom's answer is correct. I want to just chime in and add that in my scenario the reason why #with was failing was because due to the 'reactive' nature of meteor my first call to load the model resulted in 'undefined' and I didn't check for it. A fraction later it was loaded ok.
The moral is to do something like
var record = Record.findOne({whatever:value})
if (record) {
return record;
} else {
// whatever
return "loading"
}

Accessing contents of DB query

I must be missing something essential, but here is my problem. I have a Documents collection that contains 'title' and 'content' fields.
When I navigate to a particular url, say,
http://localhost:3000/document/33ea5676-4f8f-4fe4-99d5-fe094556933d
I grab the document _id from the url, store it via Session.set('docID',_id) and then want to, say, display the title of the document. I have a template:
<template name='document'>
<h2>My document is called {{document.title}}</h2>
</template>
And then in my client.js file, I have:
Template.document.document = function() {
doc = Documents.findOne({'_id':Session.get('docID')});
return doc;
}
But this does not work: I receive an error along the lines of:
Cannot read property 'title' of undefined
Because, of course, before the field can be accessed, the document must be retrieved from the database. If I call,
Template.document.document().title
from the console, I retrieve the title. I tried making a title specific function,
Template.document.title = function() {
doc = Documents.findOne({'_id':Session.get('docID')});
return doc.title;
}
But this suffers from the same problem. There seems to be a lag between when the database retrieves the entry, and in the meantime calling doc.title throws an error.
I must be overlooking something fundamental here. Thanks.
Try using 'with' in your template:
Template.document.document = function() {
return Documents.findOne({'_id':Session.get('docID')});
}
<template name='document'>
{{#with document}}
<h2>My document is called {{title}}</h2>
{{/with}}
</template>

Resources