How to associate DOM attributes to each item in Mongo Collection - meteor

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

Related

Meteor blaze, Use helper / variable in Content Blocks (contentFor)

I have a template (navBarContent) that I is passed a "title" when I insert it.
I am then able to access {{title}} within that template, but it is not possible to access it within a {{#contentFor}} block embedded in the navBarContent template:
<template name="map">
{{>navBarContent title="MAP"}}
... other content ...
<template>
<template name="navBarContent ">
{{title}}
{{#contentFor "headerTitle"}}
<h1 class="title">{{title}}</h1>
{{/contentFor}}
</template>
I already tried to "forward" the title:
<template name="navBarContent ">
{{title}}
{{#contentFor "headerTitle" title="MAP"}}
<h1 class="title">{{title}}</h1>
{{/contentFor}}
</template>
which produces the following error:
First argument must be a function, to be called on the rest of the arguments;
EDIT:
Ok. I think the data scopes are a the following:
<template name="layout">
{{> yield "headerTitleRenderedInLayout"}}
{{> yield}}
</template>
<template name='map'>
{{> yield "headerTitleRenderedInTemplate"}}
{{>navBarContent title="PARAMETER_TITLE"}}
</template>
<template name="navBarContent">
{{title}} <!-- output: PARAMETER_TITLE -->
{{#contentFor "headerTitleRenderedInLayout"}}
<h1 class="title">{{title}}</h1> <!-- output: LAYOUT_DATA_TITLE -->
{{/contentFor}}
{{#contentFor "headerTitleRenderedInTemplate"}}
<h1 class="title">{{title}}</h1> <!-- output: TEMPLATE_DATA_TITLE -->
{{/contentFor}}
</template>
Above outputs are produced when I use the following router options:
Router.route('/map', function () {
this.layout("layout", {
data: function() {
return { title: "LAYOUT_DATA_TITLE" }
}
});
this.render('map', {
data: function() {
return { title: "TEMPLATE_DATA_TITLE" }
}
});
});
My app has a navbar that is defined in my main layout and I therefore need to set the datacontext for the layout in my route. So far so good, but I want to set that data context based on the value that I pass via:
{{>navBarContent title="PARAMETER_TITLE"}}
This is just a cosmetic thing, because I prefer to define my navbar content in the different templates rather than the routes.
It works when you pass the data context to the render function because Iron Router handles the contentFor block:
Router.route('/', function() {
this.render('map', {
data: function() {
return { title: 'MAP' }
}
});
});
Then the following template shows MAP twice:
<template name="map">
{{> navBarContent }}
{{> yield 'anotherBlock'}}
</template>
<template name="navBarContent">
<div>
In "map": {{title}}
</div>
{{#contentFor 'anotherBlock'}}
<div>
In contentFor: {{title}}
</div>
{{/contentFor}}
</template>

Meteor Blaze.renderWithData how to InsertAfter is it possible?

I'm having a problem to insert a template after and not before a node. For example:
//Html looks like this
<div class="questions">
<div class="question"></div>
<div class="question"></div>
<div class="question"></div>
</div>
<template name="question">
<div class="question"></div>
</div>
<template name="questionExtraInfo">
<div class="extra"></div>
</template>
I'm trying to get the following:
<div class="questions">
<div class="question"></div>
<div class="extra"></div>
<div class="question"></div>
<div class="question"></div>
</div>
Calling blaze render inside question event
Template.question.events({
'click .more-details': function () {
var instance = Template.instance();
Blaze.renderWithData(Template.questionExtraInfo, {}, document.querySelector('.questions'), instance.find('.question')));
});
I can only figure out how render it before or inside how about after?
<div class="extra"></div>
<div class="question"></div>
<div class="question"><div class="extra"></div></div>
I think a better approach would be to take advantage of reactivity:
Change your questions template to:
<template name="question">
<div class="question"></div>
{{# if shouldIncludeExtra }}
{{> questionExtraInfo }}
{{/if}}
</template>
The above template should be inside an each loop.
Then in your js something like:
Template.question.helpers({
'shouldIncludeExtra': function() {
// replace 'n' with the actual index. I think `this.index` is
// provided within #each blocks, or you can use the new `#each` helper.
var index = n;
return Session.get('shouldIncludeExtra' + index);
}
});
Then, in your click event, you set a session var based on the index to true:
Template.questions.events({
'click .question': function(e, tpl) {
var question = e.currentTarget;
// You can probably come up with something better here..
var index = $(question).parent().find('> .question').index(question);
Session.set('shouldIncludeExtra' + index, true);
}
});
Because of reactivity, you would see the inserts right away when you fire the click event.
I realize this doesn't really answer the headline of your question, but it should get you the desired outcome.

How to call Object items in array with Mongo/Meteor?

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>

Template.dynamic is not passing data context

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>

Meteor: nested templates and a pseudo switch conditional

I'm new with meteor and at the moment i'm testing out nested templates. More specific, i'm trying to get this pseudo switch working.
I have a PARENT template that gets data from a template.helper function where it gets the data for the {{#each}}.
This is the PARENT template
<template name="result">
{{#each Tresult}}
<div class="jow">
<h3>{{name}}</h3>
<p>{{type}}</p>
<div>{{> Tstatus}}</div>
</div>
{{/each}}
</template>
The PARENT also includes another template {{> Tstatus}}
This is the CHILD template
<template name="Tstatus">
{{#status_is "green"}}
{{> Tstatus_green}}
{{/status_is}}
{{#status_is "red"}}
{{> Tstatus__red}}
{{/status_is}}
{{#status_is "orange"}}
{{> Tstatus__orange}}
{{/status_is}}
</template>
<template name="Tstatus_green">
<span>green</span>
</template>
<template name="Tstatus_red">
<span>red</span>
</template>
<template name="Tstatus_orange">
<span>orange {{number}}</span>
</template>
This template can also include 3 other templates:
Tstatus_green
Tstatus_red
Tstatus_orange
But the problem is, how do i get this pseudo switch working. So i only need to include 1 of the 3 templates, based on it's status color.
And this is the helper function for the PARENT template
Template.result.helpers({
Tresult:function(){
return Ttable.find()
}
})
I would do something like this:
Template.Tstatus.helpers({
getStatusColor:function()
{
//"this" will be the current Ttable document
var color = getColorFunction(this)
return Template["Tstatus_"+color]
}
})
<template name="Tstatus">
{{#with getStatusColor}}
{{>.}}
{{/with}}
</template>

Resources