Meteor: Displaying Categories from a List - meteor

I am new to Meteor JS and have what I believe is a simple question.
I have a list of items each of which is assigned a category. For example:
{Item: "Jump Rope", Category: "Toys"},
{Item: "Apple", Category: "Food"},
{Item: "Pear", Category: "Food"}
Etc.
In the sidebar, I want to list the categories. I thought I had found a method of how to do this, but what ends up happening is that each category is listed the number of times that it is used.
For instance, in the above example Food would be listed twice whereas Toys would be listed once. If I were to add another Food Category item (such as Tomatoes), then Food would be listed Three times in the sidebar. If I were to add a second Toy item then Toys would be listed twice.
This is not what I want. What I want is for each category to be listed once in the sidebar regardless of how many items there are.
Any ideas on how to do this?
Thanks.

You can just fetch the items and them do a pluck and a uniq on the results. Assuming you have an Items collection that contains documents which look like {item: String, category: String}, you could use the following helper:
Template.myTemplate.helpers({
categories: function() {
return _.uniq(_.pluck(Items.find().fetch(), 'category'));
}
});
Then your template could look something like:
<template name="myTemplate">
{{#each category in categories}}
<div>{{category}}</div>
{{/each}}
</template>

Related

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.

How to include top comment per each post as a separate collection in meteor?

If I have a collection of posts when entering a view of a posts collection, and each of these posts has a collection of comments on them, how could I list the top comment for each post along side of them?
E.g:
this.route('postsList', {
path: '/:posts',
waitOn: function() {
return Meteor.subscribe('posts');
},
data: function() {
return Posts.find({});
}
});
And then I'm iterating through the collection of posts on a page.
{{#each posts}}
{{> postItem}}
{{/each}}
<template name="postItem">
{{title}}
{{topComment}}
</template>
I'd like to put the top comment for each post item.
How can I do this with my templates/subscriptions/publications?
Posts and comments are separate collections.
If it were an embedded collection I could see the ease of use but how to deal with separate collections?
If I published a recent comment type of publication how could I subscribe to it for each post as the most recent one? Or am I thinking the wrong way here?
If you insist on having two totally separated collections, you would get into problems with efficient database queries. What you could do is to have something like recentComment field in your posts collection. Should this field point to id of the most recent comment related to the given post, you could alter your posts subscription to include the recent comments as well:
Meteor.publish('posts', function() {
var listOfIds = _.pluck(Posts.find({}, {fields: recentComment}).fetch(), 'recentComment');
return [
Posts.find(),
Comments.find({_id:{$in:listOfIds}})
];
});
Note that this solution is not fully reactive but it's good enough in most cases.

meteor: Iterating over a collection by field value

I have items with a category field and name field, e.g.:
{ category: 'CategoryOne', name: "ItemOne" },
{ category: 'CategoryOne', name: "ItemTwo" },
{ category: 'CategoryTwo', name: "ItemThree" },
... etc
What I would like to do is display these under a heading for the category.
I am new to meteor, and having quite the time doing two things:
(1) Getting a reactive list of categories, or
(2) Iterating through the items, displaying them grouped by category.
I'm not sure what is the correct Meteor approach here.
Unfortunately minimongo doesn't have support for aggregation yet so this is a bit difficult. The following is how I would approach it.
First create 2 template helpers. The first just puts together a list of the categories and returns an array of category names, The second takes the category name as a parameter and returns a cursor of all of the records in that category.
Template.categories.helpers({
categories: function(){
var added = [];
return Items.find().map(function (item) {
if(_(added).indexOf(item.category) === -1){
return item.category;
}
});
},
categoryItems: function(category){
return Items.find({category:category});
}
});
Next the template needs nested {{#each}} blocks with the first one iterating over the categories array and passing the category names to the next each as the parameter of the next helper.
<template name="categories">
{{#each categories}}
<h1>{{this}}</h1>
<ul>
{{#each items this}}
<li>{{name}}</li>
{{/each}}
</ul>
{{/each}}
</template>

How to show related subdocument properties in Meteor

I am new to Meteor/MongoDB and I'm trying to use something like this to describe a user and his stuff in a meteor project:
{ _id: whatever,
name: "John Doe",
myToys: [ {toy_id: "truck",
quantity: 2},
{toy_id: "legoset",
quantity: 4} ]
}
I have another collection that has all those toy_ids and their properties (manufacturer, popularity, etc. and other properties that might change later, which is why I have this in a separate collection).
How would I best code it in meteor and template it to loop through the array of toy subdocuments for a particular user and still display the associated properties of each toy?
Thanks!
We can Identify the logged in user with Meteor.userId, so if you stored that id to identify each person's toys in your toys collection so that the user can know which toys are his when using Toys.find({})
You could do this for your template helper.
Template.home.toys = function() {
return Toys.findOne({user:Meteor.userId});
}
Next you can loop through this in your template using handlebars
<template name="home">
Name: {{name}}
{{#each toys.myToys}}
Toy Id: {{toy_id}}
Toy Quantity: {{quantity}}
{{/each}}
</template>

Dependent select box meteor

I'm trying to wrap my head around the meteor dependencies and reactive variables.
I have two select boxes. One lists a category (fruit, vegetables, poultry, etc) the second will list the sub category (apples, pears, grapes, etc).
I'd like when the user changes the category dropdown to display and populate the subcategory dropdown.
I know I can watch for Template.action_form.events ={'change #category'}... but I'm not sure what steps to take from here. One thought (hack) is to output all the subcategories to a multidimensional array and use jquery to manage it. I have to think there is a smarter way to to do this with meteor.
for the category dropdown I have something like this:
Template.action_form.category = function(id){
return Category.find();
}
I'm not sure how to setup the template for the subcategory...right now I have this (not working)
Template.action_form.subcategory = function(parent){
if (document.getElementById(parent)){
category = document.getElementById(parent).value;
return Subcategories.find({category_id:parent});
}
}
The HTML/Template looks like this:
<template name="action_form">
<select id="category" class="action-selects">
{{#each category _id}}
<option value="{{_id}}">{{name}}</option>
{{/each}}
</select>
<select id="subcategory" class="action-selects">
{{#each subcategory "category"}}
<option value="{{_id}}">{{name}}</option>
{{/each}}
</select>
<template>
Thanks in for any pointers you all can offer.
if you want to use the whole reactivity magic of meteor for this, you could set an Session variable if the first select changes.
Template.action_form.events = {
'change #category': function(evt) {
Session.set("selected_category", evt.currentTarget.value);
}
}
Your subscription of Subcategories passes the selected category as a parameter into the servers publish method.
// Client
Meteor.autosubscribe(function () {
Meteor.subscribe("subcategories",Session.get("selected_category"));
}
// Server
Meteor.publish("subcategories", function(selectedCategory) {
Subcategories.find({category_id: selectedCategory})
});
The template for subcategories than displays all Subcategories if finds.
Template.action_form.subcategory = function(parent){
Subcategories.find();
};
You could, of course, publish all Subcategories at once (no idea how many you'll have there) and filter the subcategories in the client, not in the subscribe/publish methods.
Template.action_form.subcategory = function(parent){
Subcategories.find({category_id: Session.get("selected_category")});
};

Resources