Repeating Meteor template with a specific item in mongodb collection - mongodb

I have a collection with this item:
{
"item1": ["foo", "bar", "baz"],
"item2": ...
}
I made a helper function to repeat a template for each item in item1
Template.temp.helpers({
items: function() {
return Col.find({},{item1:1});
}
});
and this is the template
<template name="temp">
{{#each items}}
{{> anotherTemplate}}
{{/each}}
</template>
But I get back an empty array. Why doesn't it work?

Try with {{#each items.item1}}
Because, Col.find({},{item1:1}) just remove all other field from result.
Limit Fields to Return from a Query
It's like SELECT item1 FROM table in SQL
If you need repeat your template for each item in item1, you need to precise which array should be use.
<template name="temp">
{{#each items.item1}}
{{> anotherTemplate}}
{{/each}}
</template>

Meteor mongo methods are a little different you cannot use.
return Col.find({},{item1:1});
you can use it this way:
return Col.find({},{fields:{'item1':1}});

Maybe you want:
{{#each items}}
{{#each item1}}
{{> anotherTemplate}}
{{/each}}
{{/each}}
Or this:
Template.temp.helpers({
items: function() {
return Col.find({},{item1:1}).map(function(item){
return item.item1
})
}
});
That way items will return the item1 array. Originally it was returning an array of objects with just one element, the item1 arrays of each object:
[{
item1: ["foo","bar","baz"]
},
{
item1: ["lah","nah","jah"]
},
{
item1: ["see","gee","bee"]
}]
this way you'll get an array of arrays: [["foo","bar","baz"], ["lah","nah","jah"], ["see","gee","bee"]]

I think you need to do return something like this from helper
return Col.findOne({}).item1
It should work now
Thanks

Related

Iterating unknown object in Meteor

I am having trouble displaying data much like what is described here, but my object is not conveniently labelled in numbers: How do I iterate over an unknown object in a Meteor Spacebars template?
Data maybe be nested more than once, and might differ but always end in an array.
I am open to new data structures too.
Sample Data:
{
"Category A":
{
"Sub-Category1":
{
"Sub-Sub-Category1": ['Value1', 'Value2']
},
"Sub-Category2":
{
"Sub-Sub-Category2": ['Value3'],
"Sub-Sub-Category3": ['Value4']
}
},
"Category B":
{
"Sub-Category1": ['Value5']
}
}
You're going to need a recursive template to handle arbitrary nesting, a helper that enumerates the keys of an object, and a helper that gets the value of the parent object corresponding to a key.
html:
<template name="nest">
{{#if isArray}}
{{#each this}}
{{this}} <!-- we're assuming that array elements aren't themselves objects -->
{{/each}}
{{#elseif isObject}}
{{#each keys}}
{{#with value}}
{{> nest }}
{{/with}}
{{/each}}
{{else}} <!-- catch the case of a scalar key value (no array) -->
{{this}}
{{/if}}
</template>
js:
Template.nest.helpers({
isArray(){
return typeof(this) === "object" && this.length && this.length > 0;
},
isObject(){
return typeOf(this) === "object" && !this.length;
},
keys(){
return Object.keys(this); // returns an array of strings
},
value(){
return Template.parentData()[this]; // look up the hierarchy to get the parent object then select the data for the current key
}
});

Use data from a related collection inside a Meteor #each

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}}

Check if mongo document field exists

I want to check if the field "imageUrl" are inserted from a form into a MongoDb collection.
If it has a value, I want to return the URL, else return false. I'm not used to Mongo, and wonder how I can achieve this. Not every entry have a imageUrl field, but some do. So I need to check if it even exists for every document.
Here's my helper, where I want to have the logic:
Template.CatchesList.helpers({
catches: function () {
return Catches.find({}, {sort: {date: -1}});
},
image: function() {
if (this.imageURL) // Need proper logic here
return true;
else
return false;
}
});
And in my template:
{{#if image}}
<td><img width="100" src="{{imageUrl}}"></td>
{{/if}}
MongoDB has the $exists operator to filter results where the property does not exist in the document. So you can change your query to:
return Catches.find({ "imageUrl": { "$exists": true }}, { "sort": { "date": -1 }});
And then only items that have the property will be returned.
If you are only talking about templates, then a simple conditional test on the field returns logically where it is present or not present:
{{#each catches}}
{{> catch }}
{{/each}}
Then:
<template name="catch">
<tr>
<!-- other fields -->
<td>{{#if imageUrl}}<img width="100" src="{{imageUrl}}">{{/if}}</td>
</tr>
</template>
Where only the logic contained within {{#if}}{{/if}} will actually render.
I did this in an app and it worked for me:
Courses.find({owned_by_contract:user_contract._id}).forEach(function(doc){
var foo = ("your_field_name" in doc) ? doc.course_objective_english.value:"N/A";
}):

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});
}
});