Use data from a related collection inside a Meteor #each - mongodb

I have a collection of People that has the fields {name, id}
I also have the field Likes that contains {user_id, foodName}
I want to create a template that will display a list of People, and the food they like. I am running an each in my template to pull all of this information from a query on the Likes collection. I want to take the queried user_id from Likes and then use it to pull the associated name of the person from the People collection.
My template looks like this:
<template name="likes">
{{#each likes_list}}
//This displays the user_id, but I want to have it display the user's name
<p>{{user_id}} likes {{foodName}}</p>
{{/each}}
</template>
My helper looks like this:
Template.likes.helpers({
likes_list: function() {
return Likes.find();
}
});

I see 2 solutions:
Add userName filed to your Likes collection and display it like
this {{userName}} in your template
Create publish function which will "join" your collections (Users and Likes). To do it use this package: https://atmospherejs.com/reywood/publish-composite

You can add a helper to return the user data context and then use it with with:
html:
<template name="likes">
{{#each likes_list}}
<p>{{#with person}}{{name}}{{/with}} likes {{foodName}}</p>
{{/each}}
</template>
js:
Template.likes.helpers({
likes_list: function() {
return Likes.find();
},
person: function(){
return People.findOne({id: this.user_id});
}
});

You could map it into a single object if you wanted to.
Template.blah.helpers({
likes: function () {
return Likes.find().map(function (doc) {
return _.extend(doc, {
user: Meteor.users.findOne(doc.user_id)
});
});
}
});
This will add the user field to the collection for you to reference
{{#each likes}}
{{user.name}} likes {{foodName}}
{{/each}}

Related

How to push specific users in Mongo Collection?

I'm working on an app in Meteor, let's call it meetups. I've already done basic crud and now I'm stucked with request action when a user sends a request for invite for a meetup (I use accounts-password package to handle auth, if it means anything). I want to show meetup's owner which users would like to visit this meetup, so it's how I designed it:
I created a 'Join' button in my template and it works perfectly when I make an event in my controller like this:
'click .join': function(event) {
var params = {
user: Meteor.userId(),
username: Meteor.user().username
}
toastr.success('You've send a request');
Router.go('/meetups');
}
Since I handle this events fetching user's params, it's very easy to show his username, I do it like this:
<p>{{username}} sent a request to join your meetup</p> <button class="btn-primary btn-xs yes">Ok</button> <button class="btn-danger btn-xs no">No</button>
I don't want to just show this user, I want to let meetup's owner also manage requests (that's why I created two buttons). Basically, it's just a simple crud, isn't it? That's why I created a new Mongo collection called 'Guests' and my 'invite event now looks like this:
'click .join': function(event) {
var params = {
user: Meteor.userId(),
username: Meteor.user().username
}
Meteor.call('join', params);
toastr.success('Ok');
Router.go('/meetups');
}
In case I use call function, I have a method to handle it:
Meteor.methods({
'join': function (params) {
Guests.insert(params);
}
});
I also created anothe template called guests:
<template name="guests">
{{#each guests}}
<p>{{username}} sent a request to join your meetup</p> <button class="btn-primary btn-xs yes">Ok</button> <button class="btn-danger btn-xs no">No</button>
{{/each}}
</template>
and included it in my meetups template like this: {{> guests}}.
As you can understand since that I see no usernames. I used an iteration through guests collection and try to fetch username because it worked just fine withound defining a separate collection.
So my question is how the heck can I push specific users (in my case those who clicked join button) and show their usernames? Any help would be appreciate.
<template name="guests">
{{#each guests}}
<p>{{username}} sent a request to join your meetup</p> <button class="btn-primary btn-xs yes">Ok</button> <button class="btn-danger btn-xs no">No</button>
{{/each}}
</template>
How about add guests helper in guests template?
You didn't define it.
Template.guests.helpers({
guests: function () {
return Guests.find();
}
});

How do I list Meteor users and only show their first email address?

I'm listing all the Meteor users in a template:
<template name="userList">
<ul>
{{#each users}}
<li>
{{_id}}<br>
Emails:
{{#each emails}}
{{address}},
{{/each}}
</li>
{{/each}}
</ul>
</template>
Here's my helper:
Template.userList.helpers({
users : function() {
return Meteor.users.find({});
}
});
This works, but since I'm not using usernames, I only want to list the first email address and not have to handle it with an {{#each}} in the template. Ideally, I'd have a value for users.primaryEmail, so I changed the helper to this:
Template.userList.helpers({
users : function() {
var rawUsers = Meteor.users.find({});
var users = [];
_.each(rawUsers, function(user) {
user.primaryEmail = user.emails[0].address;
users.push(user);
})
return users;
}
});
...and updated my template code to output {{primaryEmail}}, but it doesn't seem to return any users at all now. What am I missing?
Figured out that I needed to use .fetch() in order to get the results as an array & make users.emails[0] work:
var rawUsers = Meteor.users.find({}).fetch();

MeteorJS: Autoform + CollectionFS, associating images from an FS.Collection with Corresponding Mongo.Collection Document?

I'm building a very small Meteor application simply to get a better understanding of Autoform and CollectionFS, and their use together. I currently have everything set up with the following packages:
iron:router, aldeed:autoform, aldeed:collection2, cfs:standard-packages,
cfs:filesystem, cfs:autoform
I have a sample Mongo Collection assigned to "Books" set up with a SimpleSchema attached, with fields from the demo like title and author. The corresponding code for the file upload is:
fileId: {
type: String,
autoform: {
afFieldInput: {
type: "cfs-file",
collection: "images"
}
}
}
The FS.Collection code is:
Images = new FS.Collection("images", {
stores: [new FS.Store.FileSystem("images", {path: "~/uploads"})]
});
This is in conjunction to a quickform: {{> quickForm collection="Books" id="insertBookForm" type="insert"}}
The insert is fine, and I can iterate over the documents and display the various fields using spacebars and a helper function called "books", like so:
{{#each books}}
<li>{{title}} by {{author}}</li>
{{/each}}
I can also iterate over images uploaded to the FS.Collection with a helper returning the entire collection called "files," and looping over them like so:
{{#each files}}
<img src="{{this.url}}" />
{{/each}}
The issue I'm having is linking the two together. I want to be able to do something along the lines of:
{{#each books}}
<li>{{title}}, by {{author}} <img src="The-Corresponding-Image}}" /></li>
{{/each}}
Obviously not that exact layout, but I basically just want to be able to print images with their corresponding titles and authors to be able to use autoform with collectionfs for my needs.
I'm stuck trying to retrieve the fileId from a specific document in the Books collection, and then plugging that into an Images.findOne({fileId: fileId}) and linking the two together.
Can anyone point me in the right direction?
I was able to figure it out thanks to Ethaan's guidance. What I had to do was the following:
Autoform Hook:
AutoForm.hooks({
insertBookForm: {
after: {
insert: function(error, result, template) {
insertedFile = Books.findOne(result).fileId;
Images.update({_id: insertedFile}, {$set: {'book': result}});
}
}
}
});
I set a field of 'book' to the _id of the document being inserted (stored in the result parameter) right after it was inserted.
This is my corresponding HTML:
{{#each books}}
<li>{{title}} by {{author}}</li>
{{#with files}}
<img src="{{this.url}}" />
{{/with}}
{{/each}}
And my helpers:
Template.layout.helpers({
books: function () {
return Books.find({});
},
files: function() {
return Images.findOne({book: this._id});
}
});

Selecting attributes from an object when using find() in MongoDB/Meteor

So I'm new to Meteor, and have been playing around with it, but have hit a few issues with moving over to Mongo.
Below is a simple example, where I have inserted some documents into my collection, but cannot seem to extract the attributes properly.
Here is my code (and my result vs expected result is below it)
vizart.js
ContentPieces = new Mongo.Collection("content");
if (Meteor.isClient) {
Template.loggedInDash.helpers({
content: function () {
return ContentPieces.find({});
}
});
}
vizart.html
<template name="loggedInDash">
{{#if currentUser}}
<p>Here is the content you've created</p>
<ul>
{{#each content}}
<li>{{content}}</li>
{{/each}}
</ul>
{{/if}}
</template>
Result (just pasted from the app in my browser)
Here is the content you've created
[object Object]
[object Object]
[object Object]
Expected
As you can see, I am not 100% sure how to pull out an attribute. For example, each document has a name attribute, and I'd like to spit that out in the list instead. Any help or guidance on how to select the name attribute from the content collection?
Thanks in advance!
The meteor template language (spacebars) is inspired by handlebars. I'd recommend having a look at both sets of docs, but the handlebars documentation will get you up to speed with the basic syntax.
In your example, if each document in ContentPieces has a name then you can add it to your list like this:
<ul>
{{#each content}}
<li>{{name}}</li>
{{/each}}
</ul>
I'd also recommend having a look at this post to better understand template data contexts.
In your process you are displaying the whole document, if you just want to display the name attribute you can do it by
if (Meteor.isClient) {
Template.loggedInDash.helpers({
content: function () {
var name=ContentPieces.find({}).name;
if(name)
return name;
}
});
You can just pass the field name that you want to display

One to Many Relationship (or NoSQL Mongo equivalent to it) in Meteor Collections

I am new to Mongo and NoSQL databases. Can someone explain the way to do a one to many join and a cycling through collections in Meteor.
For example, say I have two collections, a Post and a Comment where each comment has a postId, meaning each Post has zero or many Comments. I am interested in what would be best practice for this type of situation for Meteor specifically where you can cycle through each post and comment in a nested Handlebars call. Something like the example below:
{{#each post}}
{{title}}
{{content}}
{{#each comment}}
{{comment_text}} by {{author}}
{{/each}}
{{/each}}
Although the standard MongoDB paradigm is to denormalize data, in Meteor applications it's not uncommon to stick to the pattern of having different collections (tables) for each logical dataset.
To implement joins in Meteor webapps, you simply have to define a relation between the two collections :
var postId = Posts.insert({
title: "A post",
content: "Some content..."
});
Comments.insert({
postId: postId,
author: "Someone",
text: "Some text..."
});
Denormalizing means that you must not forget to publish the two collections, you can do as follow :
Meteor.publish("postById", function(postId){
// publish the according post...
var postCursor = Posts.find(postId);
// ...and every comments associated
var commentsCursor = Comments.find({
postId: postId
});
// you can return multiple cursors from a single publication
return [postCursor, commentsCursor];
});
This publication would send down to the client a post and all its comments, given a post._id.
Associated with correct client-side routing, you can subscribe to this publication with the post id retrieved from a URL (/posts/:_id) and display the post with all its comments.
Your template pseudo code is OK, however I would refactor it using a distinct template for each collection.
HTML
<template name="outer">
{{!-- loop through each post, the child template will
be using the current post as data context --}}
{{#each posts}}
{{> post}}
{{/each}}
</template>
JS
Template.outer.helpers({
posts: function(){
return Posts.find();
}
});
HTML
<template name="post">
<h3>{{title}}</h3>
<p>{{content}}</p>
{{!-- loop through each comment and render the associated template --}}
{{#each comments}}
{{> comment}}
{{/each}}
</template>
JS
Template.posts.helpers({
comments: function(){
// return every comment belonging to this particular post
// here this references the current data context which is
// the current post being iterated over
return Comments.find({
postId: this._id
});
}
});
HTML
<template name="comment">
<p>{{text}}</p>
<span>by {{author}}</span>
</template>