Hey everyone, thank you very much for your help. Question is edited per suggestions in the comments.
I'm new to Mongo and Meteor.
I have a collection "posts" with a field "slug".
The "post" template is populating correctly with each post's values. Slug value is always something like "my-great-post".
I need to get the text value for the _id's slug, which will be different each time the template is accessed, encode it, write a string, and spit the string back out into the template.
Things tried
can't return a value for "this.slug" or "this.data.slug" in either template helpers or onRendered, even though collection is defined and correctly populating spacebars values in the template
"this" returns "[object Object]" to console.log
app crashes when I try to javascript encode and deliver a string from the helper, probably I don't fully understand helper syntax from the documentation
(I followed advice in the comments to avoid trying to create scripts in the template html, so below is more information requested by everyone helping on this thread)
- Template html -
{{#with post}}
<div class="blog-article">
<div class="blog-header">
<div class="left">
<!-- title -->
<h1 class="post-title">{{title}}</h1>
<div class="holder">
<div class="post-tags">
<!-- tags -->
{{#each tags}}
<span>{{this}}</span>
{{/each}}
</div>
</div>
</div>
</div>
<div class="blog-post">
<div class="blog-copy">
<!-- date -->
<div class="post-date">{{post_date}}</div>
<!-- social -->
<div class="blog-social">
<!--
<a class="so-facebook" target="_blank" href="need to encode slug here"></a>
-->
</div>
<!-- ============== post ============== -->
{{{content}}}
<!-- ============ end post ============ -->
</div>
</div>
</div>
{{/with}}
- Template js -
Template.post.onCreated(function() {
var self = this;
self.autorun(function() {
var postSlug = FlowRouter.getParam('postSlug');
self.subscribe('singlePost', postSlug);
});
});
Template.post.helpers({
post: function() {
var postSlug = FlowRouter.getParam('postSlug');
var post = Posts.findOne({slug: postSlug}) || {};
return post;
}
// can't get these working in a helper, out of helper they crash the app
// console.log(this.slug);
// console.log(this.data.slug);
});
Template.post.onRendered( function () {
// these do not work
// console.log(this.slug);
// console.log(this.data.slug);
});
db.posts.findOne();
{
"_id" : ObjectId("576c95708056bea3bc25a91f"),
"title" : "How Meteor Raised the Bar For New Rapid-Development Technologies",
"post_date" : "May 28, 2016",
"image" : "meteor-raised-the-bar.png",
"slug" : "how-meteor-raised-the-bar",
"bitlink" : "ufw-29Z9h7s",
"tags" : [
"Tools",
"Technologies"
],
"excerpt" : "sizzling excerpt",
"content" : "bunch of post content html"
}
If some one can solve this using any method, I will accept answer with joy and gratitude most intense.
The problem is probably with the parent template, rather than this one. The way that Meteor works is that the JS files are separated from the HTML, so don't try to include a <script> tag in the HTML.
The first thing is that you have to load all of your documents into the client. (NOTE: once you've got the hang of that, then you can worry about only loading the documents that you need).
To do that, you need a collection and a publication. By default all collections are automatically published completely, so unless you removed the autopublished module, then I'll assume that it is still loaded.
So let's start with the parent template. In this case, I'm going to just loop through all of the posts in the collection and display them using the innerTemplate.
<template name=parent>
<ul>
{{#each post}}
{{> innerTemplate}}
{{/each}}
</ul>
</template>
And now our inner template might look like this:
<template name=innerTemplate>
<li>{{slug}}</li>
</template>
The end result will be a simple list with each slug.
Now, to link everything together, we need to create a JS file, which will:
1. define the collection on both client and server
2. pass the collection to the parent template
This file should be accessible to both the client and the server.
posts = new Mongo.Collection('posts');
if(Meteor.isClient) {
Template.parent.helpers({
posts() {
return Posts.find();
}
});
}
Now, if you want to do something with 'slug' in the JS file, you could do something like this:
if(Meteor.isClient) {
Template.innerTemplate.helpers({
upperCaseSlug() {
return this.slug.toUpperCase();
}
});
}
Then, you could refer to upperCaseSlug in your template, like thus:
<template name=innerTemplate>
<li>{{upperCaseSlug}}</li>
</template>
A few things about Meteor:
You should never see a pattern such as:
<script type="text/javascript">
...some code
</script>
Because Meteor combines all your js files into one big file and includes it automatically in your app. You should never have to declare your own script in this way.
Secondly, you should never have to get the value of a data object by reading the DOM. The data context of each template gives you your data in the variable this.
In either a helper or template event you can refer to this and be assured that you're going to get exactly the data being displayed in that instance of the template.
Having now seen your template code it's now apparent that your template has no data context - you set the data context inside your {{#with post}} and its associated helper but that doesn't end up creating the this you need one level below.
So... #Nathan was on the right track except that he assumed you were iterating over a cursor instead of just looking at a single post.
Take all html you have between your {{#with post}} and {{/with}} and put it in a new template, say postDetail then make your outer template:
<template name="post">
{{#with post}}
{{> postDetail}}
{{/with}}
</template>
Now your postDetail template will get a data context equal to the post object automatically and your helpers can refer to this safely.
Template.postDetail.helper({
slugURI{
return "/"+encodeURI(this.slug);
}
});
Then in your postDetail template you can get the encoded slug with:
<a class="so-facebook" target="_blank" href={{slugURI}}>
I have a site showing a list of comments and the comments can be liked by clicking a button.
The site is written using angular and the list of comments are fetched from a node back end and rendered using ng-repeat of Angular.
To record a like I need to pass some information related to the comment to the node endpoint via AJAX.
The problem is that I don't wanna store any information related to the comment on the DOM. And I did not get how I can use jQuery.data here since I am 'ng-repeat'ing to render the comments.
Please suggest some alternatives..
Here is how the comments are rendered
<div class="stickyNotes" ng-repeat="comment in commentList1">
<p>
{{comment.commentContent}}
</p>
<div>
<img src="../resources/images/thumb.png" class="upvoteImage" />
</div>
</div>
You can use $index, and pass it to your controller method onClick. Then you can access the exact comment $scope.commentList1[index] on your controller, and do what you what with it.
you can learn about $index here:
https://thinkster.io/egghead/index-event-log
I'm learning Ember right now and i'm beeing a bit confused because of the Docu of Ember and the getting started example.
In the Documentation it says:
In Ember.js, templates get their properties from controllers, which decorate a model.
And
Templates are always connected to controllers, not models.
But after doing the getting started guide i'm not sure if this is correct.
I've uploaded the finished TodoMVC app here: https://github.com/Yannic92/stackOverflowExamples/tree/master/Ember/TodoMVC
In the Index.html you'll find this template:
<script type="text/x-handlebars" data-template-name="todos/index">
<ul id="todo-list">
{{#each todo in model itemController="todo"}}
<li {{bind-attr class="todo.isCompleted:completed todo.isEditing:editing" }}>
{{#if todo.isEditing}}
{{edit-todo class="edit" value=todo.title focus-out="acceptChanges" insert-newline="acceptChanges"}}
{{else}}
{{input type="checkbox" checked=todo.isCompleted class="toggle"}}
<label {{action "editTodo" on="doubleClick"}}>{{todo.title}}</label>
<button {{action "removeTodo"}} class="destroy"></button>
{{/if}}
</li>
{{/each}}
</ul>
</script>
My question refers to the 3rd Line:
{{#each todo in model itemController="todo"}}
The Controller todo is only needed to provide the actions for the todos. The data is accessable even without this controller.
In my opinion there is the model directly connected with the template isn't it?
Or is there a default Controller like the docu mentioned here?
For convenience, Ember.js provides controllers that proxy properties from their models so that you can say {{name}} in your template rather than {{model.name}}.
As you can see in this line: <script type="text/x-handlebars" data-template-name="todos/index"> this is the template for / because the router has this line: this.route('todos', { path: '/'}). Which will have a controller named TodosController, even if you didn't write one ember will generate one for you. So when you delete it that's what happens.
In this template you loop through the todo's list. Each of these Todo models are decorated with a controller the TodoController. And with this line: {{#each todo in model itemController="todo"}} you tell ember to use this TodoController for every element in the list.
If you leave out the itemController ember assumes the todo's are part of the model for the IndexController provided by the IndexRoute.
By default ember has an empty controller which proxies everything to the underlying model. (Note: I believe this will change in ember 2.0). So it may look like it's directly coupled to the model. But you could write a controller that changes everything without changing the model.
I am converting someone else's code to Handlebars.js and I'm stuck on converting this tag to its {{#handle-bar}}{{/handle-bar}} counterpart.
The previous coder used an {{#ifCond}} to toggle what 'selected'. This is my component.
{{#dropdown-item }}
{{unbound this.itemName}}
{{/dropdown-item}}
Here is the div i want converted to my component
<div class="dropdownItem" {{bind-attr value=formField_DropdownItemID}}{{#ifCond formField_DropdownItemID value}} selected{{/ifCond}} >
{{unbound this.itemName}}
</div>
My first thought was to just pop the div's logic into the the component, like the next example, but this gave me an error.
{{#dropdown-item bind-attr value=formField_DropdownItemID {{#ifCond formField_DropdownItemID value}} selected{{/ifCond}} }}
{{unbound this.itemName}}
{{/dropdown-item}}
Any suggestions?
You can set those properties to compute. The syntax would be:
{{#dropdown-item selected=computedProperty value=formField_DropdownItemID}}
computedProperty can deal with your conditional logic. The whole idea is to pull that out of handlebars anyways. :)
I've been working through the Discover meteor book, currently at chapter 6. I'm having great difficulty in understanding the relationship between templates, and how they work.
For example, I have a template called posts_lists.html and a javascript file called posts_lists.js
Within posts_lists.js I have the following:
Template.postsList.helpers({
posts: postsData
});
And within posts_lists.html I have:
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
</div>
</template>
So, am I right in thinking that Template.postsList.helpers({ is a block that contains all the variables accessed by the postsList template. The bock returns postsData in the form of posts.
posts is looped over - calling the postItem template. This is where I get lost... as I don't have a postItem.js file, or postItem.html file
I do have post_item.html and post_item.js - but these aren't named the same as postItem...
....but it works???? I don't get it. I know its not magic... but cant figure it out. In terms of my level / skillset I'm a designer / jquery user trying to learn more.
Thanks,
Rob
The file names have little significance. It's just a convention.
Your post_item.html should contain a template named postItem.