formatting spacebars outputs - meteor

I'm using meteor/spacebars and trying to format the output from an {{#each}}. I've found this question previously asked here but I've tried to apply the answers and for what ever reason they just clear the fields that were supposed to be formatted to give no output at all and I can't for the life of me figure out why.
This is what my mongo entries look like:
{ "changes": [
{
"name": "oBf4vPN6TcGw8mbok",
"date": "2016-06-07T01:48:37.695Z",
"score": "general",
"value": "5"
},
{
"name": "oBf4vPN6TcGw8mbok",
"date": "2016-06-07T01:48:38.361Z",
"score": "general",
"value": "-5"
}
}
and this is the HTML:
{{#each changes}}
<tr>
<td>{{name}}</td>
<td>{{date}}</td>
<td>{{score}}</td>
<td>{{value}}</td>
</tr>
{{/each}}
I'm trying to format the 'date' into something more readable and the 'name' field to find username of the user that corresponds to that code. It looks like I'm going to want to apply 'register helpers' which I haven't used before and can't find much information about -except here- but when I copy and paste the following code into my client-side javascript file it simply clears the output and returns and blank space
UI.registerHelper('formatTime', function(context, options) {
if(context)
return moment(context).format('MM/DD/YYYY, hh:mm');
});

Josh,
Here is a working sample meteor application I put together to show you how this works:
Working Test Project:
Moment JS and Meteor Test - on Github
The two files you're mostly interested in are:
main.html and main.js
This above example gives the following output:
It is now (unconverted): 1465357853653
It is now (converted): June 7th, 2016
It was as some time (unconverted): 2016-06-07T01:48:37.695Z
It was as some time (converted): June 6th, 2016
Now to my lengthy answer:
I'm going to assume you have a file like this:
some_template.html:
-------------------
<template name="someTemplate">
{{#each changes}}
<tr>
<td>{{name}}</td>
<td>{{date}}</td>
<td>{{score}}</td>
<td>{{value}}</td>
</tr>
{{/each}}
</template>
Make another file like this:
helpers.js:
-----------------
Template.registerHelper('ISOToHuman', ( isoString ) => {
if ( isoString ) {
return moment( isoString ).format( 'MMMM Do, YYYY' );
}
});
And then use it like this:
{{ISOToHuman "ISO string here"}}
In your case:
{{#each changes}}
<tr>
<td>{{name}}</td>
<td>{{ISOToHuman date}}</td>
<td>{{score}}</td>
<td>{{value}}</td>
</tr>
{{/each}}
Also make sure you have momentjs installed. Run this in your terminal/console:
$ meteor add momentjs:moment
(note: '$' is the console cursor, do not include it when running the command)
Sources:
Here are links to the pages that helped me create this answer for you:
Test Github project setup for this question
Meteor Chef - Using Global Template Helpers
Atmosphere: momentjs:moment

Related

Iterating through user accounts in meteor accounts UI

I wanted to display all the user information in a tabular format as a part of an admin page. I used meteor accounts ui package for the same.
The HTML code is:
{{#each userList}}
<tbody>
<tr>
<th scope="row">*</th>
<td>{{infofullname}}</td>
<td>{{infosurname}}</td>
<td>{{infoemail}}</td>
</tr>
</tbody>
{{/each}}
The problem is that the information for the current user is getting displayed and not all the enrolled users. The iteration does happen but for the current logged in user. Also the email address is not getting displayed.
The helper code is:
Template.students.helpers({
userList: function(){
return Meteor.users.find({});
},
infofullname: function(){
return Meteor.user().profile.fullname;
},
infosurname: function(){
return Meteor.user().profile.surname;
},
infoemails: function(){
return Meteor.user().emails.[0].address;
}
});
Im facing the following problems:
1) The email address is not getting displayed.
2)The information of all the users is not getting displayed.
Thank You.
Publish all users with the following on the server:
Meteor.publish('allUsers',function(){
return Meteor.users.find({},{fields: {emails: 1, profile: 1}});
this.ready();
});
Then subscribe on the client with:
Meteor.subscribe('allUsers');
Your helpers will need some slight modifications as #Sudhanshu suggested however since you're looping over a cursor of users you can take advantage of this being an individual user object inside the loop.
Template.students.helpers({
userList() {
return Meteor.users.find({});
},
infofullname() {
return this.profile.fullname;
},
infosurname() {
return this.profile.surname;
},
infoemails: function(){
return this.emails.[0].address;
}
});
You can also access nested properties directly in blaze, avoiding the need for three of your helpers, ex:
{{#each userList}}
<tbody>
<tr>
<th scope="row">*</th>
<td>{{profile.fullname}}</td>
<td>{{profile.surname}}</td>
<td>{{emails.[0].address}}</td>
</tr>
</tbody>
{{/each}}
Multiple things are wrong:
Meteor.users() will give you multiple users only if you publish them (or you use autopublish).
Meteor.user() will always give you only the currently logged in user. So all your helpers will not work as per your plan. Modify them to use Meteor.users.findOne({_id: id)}). You can always use helpers with parameters.
Meteor publishes only the profile by default and not the emails. So you will have to publish the emails field in your publication.

Meteor: what is "data context"?

Just starting with Meteor, and going through the Meteor Tutorial by Matthew Platts.
In this tutorial, as well as in the official Meteor Documentation, there are many references to the concept of data context, but I can't seem to find a cristal clear definition / explanation (with examples) of what this is.
For instance, in the 2.4.3 Rendering Data with Helpers section, we read:
Notice that inside of the #each block we go {{name}}, even though
we have no name helper defined. This is because the data context
changes inside the #each block. We loop through each item in the
teams array, and since each item has a “name” attribute Meteor will
automatically create a {{ name }} helper.
UPDATE: Actually, just reading through the end of this very section, the author recommends a resource that makes things pretty clear: A Guide to Meteor Templates & Data Contexts. Still no accurate definition though.
So, in Meteor, what is data context exactly?
I'll try to explain as much as I know, correct me if I'm wrong.
I'll explain using following snippet:
<template name="posts">
{{#each posts}}
<p>{{name}}</p>
{{/each}}
</template>
Let's assume it will display all the posts names from a blog:
First Post
Second post
Third post
..........
..........
I assume you know the concept of helpers and events.
In the above snippet, in general for {{name}}, meteor searches for the helper called name in helpers:
Template.posts.helpers({
name: function(){
return "dummy text";
}
});
If it finds any, it runs that helpers and displays the value.
So here, it outputs:
dummy text
dummy text
dummy text
...........
But if it doesn't find any helpers, it will search in data context.
Let's assume for posts we're returning some data:
Template.posts.helpers({
posts: function(){
return Posts.find().fetch();
}
});
The data we're sending to posts helper looks like this:
{name: "First post", _id: "xxx", ......},
{name: "Second post", _id: "yyy", ......}
{name: "Third post", _id: "zzz", ......}
.................
In the code for {{#each posts}}, it loops through every object and displays name property("First Post","Second Post,"Third Post").
It displays name property because it doesn't find any helper for name, and then it searches in the current data context and found property with the same name name and displays that.
Data context in helpers and events
Let's take the same snippet and add a delete button:
<template name="posts">
{{#each posts}}
<p>{{name}}</p>
<button>Delete Post</button>
{{/each}}
</template>
It displays like below:
First Post <Delete Post>
Second post <Delete Post>
Third post <Delete Post>
..........
..........
In the events:
Template.posts.events({
'click button': function(){
console.log(this)
Posts.remove({_id: this._id });
}
})
Here, when you click on any delete button, it will delete respective post.
Here we're using this._id: this means data context.
this will give you the data that the helper takes as input to display.
For example, if you click on the delete button beside First Post, then in the events it will give you following data as this:
{name: "First post", _id: "xxx", ......},
because that is the data context available when it displays that content.
Same if you click on the button beside second post:
{name: "Second post", _id: "yyy", ......},
And same goes with the helpers too.
I hope this helps at least someone out there.
This is not easy to explain. Like you I used it in tutorial without knowing it. After some research I found the best explanation, a visual one. You can have a look at Discover Meteor article about "Templates & Data Contexts". Hope it will clarify your mind about it.
A data context can be one of 3 things: (unless I've missed some)
A cursor, i.e. the result of a Collection.find()
An array of objects, i.e. just some array or the result of a Collection.find().fetch()
An individual object, i.e. { _id: "123", name: "Orthoclase E. Feldspar" }
{{#each foo}} loops over a cursor or array context and changes the context to an individual object. {{#with bar}} just says which helper to use (in this case bar) to set the data context.
During development, but especially while learning Meteor, it helps to have console.log(this) at the top of your helper code just to double check what the data context is. It is this.

Create/modify Meteor templates at runtime

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).

Meteor Iron:Router Problems Getting ID

Can someone explain why am i getting the full value of ObjectID instead just the clean ID?
This is what i'm getting:
And the HTML output:
My Post
I haven't done anything out of the ordinary. Very basic stuff right now. Just trying out Meteor for the first time.
Routing: lib/router.js
// Dashboard
Router.route('/dashboard', {name: 'dashboard'});
// Post detail
Router.route('/summary/:_id', {
name: 'postSummary',
data: function() {
return Post.findOne(this.params._id);
}
});
List page template: templates/posts/post_dashboard.html
{{#each posts}}
<tr>
<td>
<p>{{title}}</p>
<p><small>Created at {{createdAt}}</small></p>
</td>
...
</tr>
{{/each}}
Detail page template: templates/posts/post_summary.html
<template name="postSummary">
{{> postHeader}}
<h3>{{title}}</h3>
</template>
Template helper: templates/posts/posts.js
Template.dashboard.helpers({
posts: function () {
return Post.find({});
}
});
And here's the packages i have installed, just in case it's necessary.
meteor-platform
autopublish
insecure
matthew:foundation5-sass
iron:router
jquery
useraccounts:core
useraccounts:foundation
accounts-password
accounts-facebook
accounts-google
accounts-ui-unstyled
aldeed:autoform
aldeed:collection2
forwarder:autoform-wizard
fortawesome:fontawesome
When you query the Posts collection from the console, is the _id in the returned document(s) a string literal or an ObjectId Object?
Assuming it's the latter, that's why this is happening, and if so, it's probably going to be because you've populated the collection by using insert in the Mongo shell (or alternatively restored from an existing MongoDB). By default, Meteor insert uses strings for the auto-added id when the id is not specified, whereas Mongo uses ObjectIds.
Hope that helps, but let me know if I'm on totally the wrong track!
Mongo ObjectID has a property: _str It includes string representation of the ID.

Handlebars + Meteor + iron-router

I am using iron-router for my meteor project and everything was going fine but I just ran into some strange behavior.
I have a loop set up for a list of items that looks something like this.
{{#each list_items}}
<div>{{user.username}}
Click here!
</div>
{{/each}}
The JSON object for my user looks something like this:
{
user: {
username: jdoe
},
images: {
low-res-url: http://example.com
},
link: http://example.com/profile
}
Now the {{user.username}} shows up as expected but when I try to put the {{link}} in the href I get an error from iron-router saying
"You called Router.path for a route named undefined but that that route doesn't seem to exist. Are you sure you created it?"
Any help or advice would be appreciated.
Under the hood Iron-Router registers handelbars helper:
Handlebars.registerHelper('link', function (options) {
...
});
Simply change field link to different name like my_link.
As #perhelium mentioned Iron-Router has specified a helper named 'link'
Handlebars.registerHelper('link', function (options) {...});
In order to access an item named 'link' in your JSON object you need to explicitly refer to the JSON object itself.
So your line: Click here!
Would need to be specified as Click here!

Resources