In my Mongo database, I have products and each of these products have images.
I would like to know how I can call each image in put them in a Meteor template. I know things like {{#each}}{{/each}} exist, but I don't know how to capture the images object.
With this code...
Template.products.helpers({
product: function () {
return ProductList.find();
}
});
I am able to call the first image like:
<img src="{{images.[0]}}" alt="{{title}}"/>
I could hardcode the rest of the images, but I would like to do this dynamically. Any tips?
EDIT: Here is the expected HTML output (the extra classes on .content__product are added dynamically via a separate function):
<section class="content__products">
<figure class="content__product selected">
<img src="/images/product/tt-shirt-01.jpg" alt="Time Travel Tee">
</figure>
<figure class="content__product move-down">
<img src="/images/product/tt-shirt-02.jpg" alt="Time Travel Tee">
</figure>
<figure class="content__product">
<img src="/images/product/tt-shirt-03.jpg" alt="Time Travel Tee">
</figure>
</section>
each works for both cursors and arrays, so you can do something like this:
{{#each products}}
<h2>{{title}}</h2>
{{#each images}}
<img src={{this}} alt={{../title}}>
{{/each}}
{{/each}}
Recommended reading: A Guide to Meteor Templates & Data Contexts
{{#each}} only accepted array or cursor of Mongo collection. So you have to change your object to array and make another template.
This is sample code for you.
if(Meteor.isClient){
Template.product.helpers({
list : function(){
return Products.find({});
}
});
Template.productImg.helpers({
imgList: function(){
var imgs = [];
_.each(this.images, function(img){
imgs.push({imgPath: img});
});
return imgs;
}
});
}
<template name="product">
{{#each list}}
{{> productImg}}
{{/each}}
</template>
<template name="productImg">
{{#each imgList}}
<img src="{{imgPath}}" alt="{{../title}}"/>
{{/each}}
</template>
For an individual product page I would do it like this:
Template.products.helpers({
'products': function() {
return ProductList.find();
}
});
Multiple images per product:
<section class="content__products">
{{#each products}}
<figure class="content__product">
{{#each images}}
<img src="{{this}}" alt="{{../title}}">
{{/each}}
</figure>
{{/each}}
</section>
Single image per product:
<section class="content__products">
{{#each products}}
<figure class="content__product">
<img src="{{images.[0]}}" alt="{{../title}}">
</figure>
{{/each}}
</section>
Okay, so the code posted here helped me out, but didn't solve my issue out of the box. Here is how I changed my code:
if (Meteor.isClient) {
Template.products.helpers({
product: function () {
return ProductList.find();
}
});
Template.product.helpers({
imgList: function () {
var imgs = [];
_.each(this.images, function (img) {
imgs.push({imgPath: img});
});
return imgs;
}
});
}
And, my template:
<template name="product">
<section class="content__products">
{{#each imgList}}
<figure class="content__product">
<img src="/{{imgPath}}" alt="{{../title}}"/>
</figure>
{{/each}}
</section>
</template>
Related
am trying to list 9 items each time and i found alethes:pages that can help accomplish it.
i didn't understand how exactly it should be implemented.
My collection is created with
Products = new Mongo.Collection("products");
so i created the Pages inside /lib folder
Pages = new Meteor.Pagination(Products, {
perPage: 9,
sort: {
createdAt: -1
}
});
Am confused on how to use the Pages to list the items? below is my template to list the items. Am calling the template inside another template to list the items.
This is the template
<template name="list_products">
{{#each applications}}
<div class="col-sm-4 col-lg-4 col-md-4">
<div class="thumbnail">
<img src="{{previewImage}}" alt="">
</div>
</div>
{{/each}}
</template>
How do i apply Pages to list_products template?
sorry for my english
This how you have to define your template and item template for pages.
Pages = new Meteor.Pagination(Products, {
perPage: 9,
sort: {
createdAt: -1
}
templateName: "list_products",
itemTemplate: "list_products_item",
});
Then your list_products have the following templates {{> pages}} and {{> pagesNav}} like this :
<template name="list_products">
{{> pages}}
{{> pagesNav}}
</template>
And here how you have to create your each item template which will show your item and you don't have to use any #each for that. Basically it will render each time based of your item per page value:
<template name="list_products_item">
<div class="col-sm-4 col-lg-4 col-md-4">
<div class="thumbnail">
<img src="{{previewImage}}" alt="">
</div>
</div>
</template>
The code below should insert the selected item value in the Tasks collection, retain the info for later use, and a headerLabel should show the task selected. I am not able to get the headerLabel to show the task when the click .menuItem function runs. Thanks
Tasks = new Mongo.Collection('tasks');
Template.mainMenu.events({
'click .menuItem': function(event){
Tasks.insert({menuItem: $(event.currentTarget).data('value')});
}
});
Template.header.helpers({
headerLabel: function( id ){
return Tasks.findOne({_id: id}).menuItem;
},
tasks: function(){
return Tasks.find();
}
});
<template name="mainMenu">
<div class="container">
<div class="row">
<section class="col-xs-12">
<div class="list-group">
{{#each menuItems}}
<a href="#" class="list-group-item menuItem" data-value={{menuItem}}>
<img src="/abc.png">
{{menuItem}} <span class="badge">></span>
</a>
{{/each}}
</div>
</section>
</div>
<template name="header">
<h1><button class="col-xs-2 mainMenu" type="button">☰</button></h1>
<h3>
<label class="col-xs-8 text-center">
{{#if headerLabel}} {{headerLabel}} {{else}} Select an item {{/if}}
</label>
</h3>
<h1><button class="col-xs-2" type="button">⋮</button></h1>
</template>
Assuming that the click can happen multiple times, you'll need to pass an _id for the appropriate task into your helper:
Tasks = new Mongo.Collection('tasks');
Template.mainMenu.events({
'click .menuItem': function(event){
Tasks.insert({menuItem: $(event.currentTarget).data('value')});
}
});
Template.header.helpers({
headerLabel: function( id ){
var task = Tasks.findOne({_id: id});
if( task ) {
return task.menuItem;
}
},
tasks: function() {
return Tasks.find();
}
});
So what I'm doing there is saying find one task's menuItem that has the ID passed to the helper. I also added a tasks helper to get all tasks. So your template might look something like:
<template name="mainMenu">
{{#each task in tasks}}
<h4>{{headerLabel task._id}}</h4>
<!-- Additional code here... -->
{{/each}}
</template>
You'll obviously need to customize the HTML to your specific situation, but that should give you the gist. Hope that helps!
How can I give some HTML attributes to each item in a Mongo Collection in Meteor? My use case is:
I have a collection of documents (pdfs)
I want to display a thumb for each of them
On the template, each document is an article.document which is absolutely positioned
I want to assign a Math.random() value for the "top" and "left" for each article.document
My code: http://meteorpad.com/pad/bq6Ph5CQXMMejFQiF/DocumentList
document-list.html:
<template name="documentList">
{{#each documents}}
<article class="document {{#if active}}active{{/if}}">
<header class="document-header">
<div class="document-avatar btn-floating lighten-3"></div>
</header>
</article>
{{/each}}
</template>
document-list.js:
Template.documentList.helpers({
documents: function() {
return Documents.find({});
}
});
My doubt is: where should I do the calculation for the random values of the article.document elements and when should I assign the values to the DOM nodes.
Thanks!
First, you should separate out the document template:
<template name="documentList">
{{#each documents}}
{{> document}}
{{/each}}
</template>
<template name="document">
<article class="document {{#if active}}active{{/if}}">
<header class="document-header">
<div class="document-avatar btn-floating lighten-3"></div>
</header>
</article>
</template>
Now you can create a rendered function that will be called for each document that's rendered into the DOM:
Template.document.rendered = function() {
var $article = $(this.find('article'));
// Add the position attributes etc. using JQuery
$article.css({
position: "absolute",
top: 10, left: 10
});
}
I have a list template (#each) in a package that I plan to use across many different collections. Since the template is in a package they are not easily customizable. So I figured this was a great example to use Template.dynamic. Everything works except passing data.
.. I pull the data into the routed page and manipulate the data to match the dynamic template.
Template.usersIndex.helpers({
items: function() {
var users = Meteor.users.find({}).fetch();
var items = users.filter(function(user) {
return user;
}).map(function(user){
return {
name: user.profile.name,
description: user.emails[0].address,
tidbit: "hello"
};
});
return items
}
});
... the data passes perfectly to the usersIndex template.
<template name="usersIndex">
<div id="gc-users-index-navbar">
<h2>Title</h2>
</div>
<div id="gc-users-index" class="inner-content">
{{> Template.dynamic template="strataIndexItem" data="items" }}
</div>
</template>
... But no dice, the dynamic template is rendered but no data.
<template name="themeIndex">
<div class="list-group">
{{#each items }}
<div class="list-group-item">
<div class="row-content">
<div class="least-content">{{tidbit}}</div>
<h4 class="list-group-item-heading">{{name}}</h4>
<p class="list-group-item-text">{{description}}</p>
</div>
</div>
<div class="list-group-separator"></div>
{{/each}}
</div>
</template>
You pass data as string?
{{> Template.dynamic template="strataIndexItem" data="items" }}
You should pass data as variable, without ""
{{> Template.dynamic template="strataIndexItem" data=items }}
Also check if your strataIndexItem template is named strataIndexItem:
<template name="strataIndexItem">
...
</template>
rather a theoretical question - how can I render recursive templates in Meteor? For example, displaying a comment on comment with unlimited number of comment sub-levels so that HTML would be diplayed as the following?
<section>
some text
<section>
nested text
<section>
further nested text and sections
.....
</section>
</section>
</section>
In my case I pass to the "tree" template a mongoDB document and this document can have unlimited number of sub-content levels. My example below doesn't work the way I want.
<template name="tree">
<div class="wrapper" style="border:1px solid red">
<ul>
{{#each getStructure}}
<li>
{{#each content}}
<ul>
<li>
<a class="item">{{text}}</a>
<!-- TODO: this stuff needs to be recursive.
{{#if sub_content}}
<ul>
{{#each sub_content}}
<li>
<a class="item">{{text}}</a>
{{#if sub_content}}
....
{{/if}}
</li>
{{/each}}
</ul>
{{/if}}
</li>
</ul>
{{/each}}
</li>
{{/each}}
</ul>
</div>
</template>
A simplified example of recirsuve, say you had a post sample template of:
<template name="post">
{{post_text}}
{{#each comments}}
{{>comment}}
{{/each}}
</template>
and a post helper of:
Template.post.helpers({
comments: function() {
return CommentCollection.find({post_id: this._id, parent_id: {$exists: 0}});
}
});
I would create a template for the comment layout and provide a helper in that for sub-comments, depending on your data structure something like the following:
<template name="comment">
{{comment_text}}
{{#each sub_comment}}
{{> comment}}
{{/each}}
</template>
and then the helper along the lines of:
Template.comment.helpers({
sub_comments: function() {
return CommentCollection.find({parent_id: this._id});
}
});
This would recursively produce the comments template for each sub-comment then roll back up the tree to the next #each and then print that comment and all of its sub-comments etc.