How to show related subdocument properties in Meteor - 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>

Related

Displaying data from two collections using each block

So I have 2 collections as follows
Collection user, a document might look like this
{_"id":"xyz",
"name":"sam"
}
Collection items, more that one documents linked to user collection as follows
{_"id":"123345",
"userid":"xyz"
"item":"potato"
}
{_"id":"3456",
"userid":"xyz"
"item":"tomato"
}
now lets say i am running a query to display all documents with name sam as follows, (will be run as a helper)
return User.find({name:"sam"});
and I pass the records into blaze template and display value using {{#each}}.
Now, additionally I want to also display data from collection "items" along with collection "users". So my data in html after using {{#each}} might look like this
<li> sam ...potato, tomato </li>
<li> sam ...potato, tomato, orange </li>
<li> sam ...pineapple </li>
<li> sam ... </li>
i.e data is being displayed on the template using #each but from 2 different collections.
Can anyone tell me how the code in my template should look like?
You can use publish-composite package when you make your users publication. (See example 1 in the link)
So for your use case something like should work:
Meteor.publishComposite('userItems', {
find: function() {
return Users.find({
name: "sam"
},
{
sort: {
...
},
fields: {
...
}
});
},
children: [{
find: function(user) {
return Items.find({
userid: user._id
}, {
fields: {
"item": true,
...
}
});
}}]
});
The solution here is to pass a parameter to the helper. Here it should happen a little bit trickier:
1)Know how to pass parameters:
lets name the helper products
HTML
{{products paramA}}
JS
products: function(userId){
findOne({userId:userId})
}
2)Perfect, but how to pass the _id:
As you are calling the users helper with each you have access by {{_id}} so it should be possible to pass it.
If this somehow does not work. Again try to use the each. Try to reach the element in the products helper by this._id.
I hope the two ways will be enough to solve your problem.

Use Flow Router Param in Autoform

Friends,
I'm working on my first app in Meteor and hitting my head against the wall on something...
I have a scenario similar to a blog + comments situation where I have one collection (call it 'posts') and want to associate documents from another collection (call it 'comments').
The best way I know to pass the post._id to the comments as a "postId" field is to use the Flow Router params, since the form is on the 'post/:id' view.
But for the life of me, I cannot figure out how to get "var postId = FlowRouter.getParam('postId');" to pass to Autoform so it populates. I've tried adding it as a function in the schema, as a hook, and as a hidden field in the form on the page (obviously don't want to go that route).
Autoform is amazing and I want to use it, but may have to wire it up the hard way if I can't get this darn value to populate.
Any ideas? I've been hitting my head against the wall on this for a couple of days now.
Thanks!
First, just so we're on the same page, if you have your route is set up like this:
FlowRouter.route('/blog/:postId', {
action: function (params, queryParams) {
FlowLayout.render('layout', { body: 'postTemplate' });
},
});
You are able to call FlowRouter.getParam('postId') from inside the AutoForm hook
You'll need to use an AutoForm hook and have a complete schema. I'm using the package aldeed:collection2 for the schema set up. The postId field must be explicity declared. This code is running on both server and client.
Comments = new Mongo.Collection("comments");
Comments.attachSchema(new SimpleSchema({
comment: {
type: String,
label: "Comment"
},
postId: {
type: String
}
}));
Setting your form up like this is not what you want:
{{> quickForm collection="Comments" id="commentForm" type="insert"}}
That's no good because it will show the postId field in the HTML output. We don't want that, so you have to fully define the form like this:
{{#autoForm collection="Comments" id="commentForm" type="insert"}}
<fieldset>
{{> afQuickField name='comment' rows=6}}
</fieldset>
<button type="submit" class="btn btn-primary">Insert</button>
{{/autoForm}}
Then add the AutoForm hook. This code is running on the client.
var commentHooks = {
before: {
insert: function(doc){
var postId = FlowRouter.getParam('postId');
doc.postId = postId;
return doc;
}
}
};
AutoForm.addHooks(['commentForm'],commentHooks);
Make sure you have your allow/deny rules set up, and it should be working fine.
I was struggling with this same use case as well, and I found this on the Meteor forums: https://forums.meteor.com/t/use-flow-router-param-in-autoform/14433/2
If you're using a schema to build your form (either with the autoform or quickform tags) then you can put it right in there.
For example:
campaignId: {
type: String,
autoform: {
value: function() {
return FlowRouter.getParam('campaignId');
},
type: "hidden"
}
},

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.

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>

Meteor reusable components

Meteor's reusable UI components is still far away on the roadmap. What's the best community approved way to create reusable components? A system based on Session seems so global.
Let's say I want to create 2 different chat channels on 1 page at the time. What do I do?
Presuming you're doing chat through collections...
I would make it such that a certain value is added to the chat JSON going into the MongoDB. Say for instance, user test sends a message hello world in chat box 1. The JSON I'd send would look something like
{name: 'test', message: 'hello world', num: 1}
Then, in my chat helper thing, wherever I'm displaying new chats, I'd use a get method like this
UI.registerHelper(getChat, function(n){return Messages.find({num: n});})
Which would be called in the HTML with
{{#each getChat 1}} or {{#each getChat 2}} or whatever, depending on how many chat boxes you have.
This would basically only return values that correspond to the specific chat box.
Good luck.
If you want to use something dynamic instead of fixed param value, you can try this
// In memory collection for demonstration purpose
Messages = new Meteor.Collection(null);
if (Messages.find().count() === 0) {
Messages.insert({name: 'test2', num: 1});
Messages.insert({name: 'test5', num: 2});
// [...] Init some test data
}
Template.test.helpers({
// you can also use a collection
chats: function() { return [{num:1}, {num:2}] },
});
// Took from Bob's example
UI.registerHelper('getChat', function(n){
return Messages.find({num: n});
});
And your template
<template name="test">
{{#each chats}}
{{! num refer to a property of 'this'}}
{{#each getChat num}}
{{name}} <BR />
{{/each}}
{{/each}}
</template>
You can read more about Custom Block Helpers in Blaze here

Resources