in Meteor.template.rendered how to make a call after everything is rendered (after the {{#each}} loop finishes) - meteor

Hi I have this template
<template name="users">
{{#each user}}
<li id="{{_id}}">
<input type="checkbox" checked />
<span><select name="colorpicker">
{{#each color}}
<option value="{{mColorCode}}" {{selected ../mColor mColorCode}}>{{mColorName}}</option>
{{/each}}
</select>
</span>
<img width="40" src="data:image/png;base64,{{mImage}}" />
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
{{/each}}
</template>
What i want to do is after the template is rendered, i want to convert the dropdown to a colorpicker. I'm using a jquery plugin
Template.users.rendered = function(){
$('select[name="colorpicker"]').simplecolorpicker({picker: true});
}
The problem is sometimes its not working, (Sometimes the call is being made before the dom being ready.)
I want to call this plugin after everything is rendered. and not for each user added, how can i do this ?
Thanks

I've found Meteor to be a little funny with how often it renders templates, and jQuery functions can end up building up.
I've taken to adding console.log("users rendered"); to understand how many times and when the render callback is triggered.
One thing I've had some success with is wrapping that template inside another, and then tying the callback to the outside template. Something like this:
<template name="container">
{{> users}}
</template>
<template name="users">
{{#each user}}
<li id="{{_id}}">
<input type="checkbox" checked />
<span><select name="colorpicker">
{{#each color}}
<option value="{{mColorCode}}" {{selected ../mColor mColorCode}}>{{mColorName}}</option>
{{/each}}
</select>
</span>
<img width="40" src="data:image/png;base64,{{mImage}}" />
<span class="name">{{mUsername}}</span>
<p><span class="description">{{mDescription}}</span></p>
</li>
{{/each}}
</template>
And this just add the callback to the container
Template.container.rendered = function(){
$('select[name="colorpicker"]').simplecolorpicker({picker: true});
console.log("rendered");
}
Not totally sure why it works, but it has for me, hopefully someone can illuminate us both.

This is the solution that I used, it's also a hack if someone has a better solution please post it.
What i did was i put the rendered template in the callback of the subscribe of my collection.
So my code was something like this :
Meteor.subscribe('trackedUser',function(){
Template.users.rendered = function(){
......
}
}

I see this is a pretty old post, but since I had a problem and I think I've found a better way, here it is. The containing template will technically be rendered even while the contents are rendering. In my case I was trying to initialize a materialize modal. To make it work It had to do the following.
template.html
<div class="row" id="eventResults">
{{#each eventResults}}
{{> eventResult}}
{{/each}}
</div>
<template name="eventResult">
<div class="modal event" id="{{id}}">
<div class="modal-content">
...
and template.js
Template.eventResult.onRendered(function(){
this.$('.modal-trigger').leanModal();
By calling the code on the onRendered of the child element and using this.$('...'), the code doesn't get called multiple times for each element, just once each.

Related

Nested #Each values are not available outside of the context

I'm having a nested each pair like this:
{{#each goal in goals}}
<template name="task">
{{#each goal in goals}}
{{#each task in relatedTasks goal}}
<li>
<span class="text task"><strong>{{task.taskName}}</strong> to {{goal.goalName}}<br> taskid: {{task._id}}
{{task.taskPostpone}}</span>
{{#afModal class="btn btn-primary" collection="Tasks" operation="update" doc=task._id}}
Update {{task.taskName}}
{{/afModal}}
</li>
{{/each}}
{{/each}}
</template>
and would like to get the value of the task._id in my client.js like here:
Template.task.events({
'click .task': function() {
Session.set("selectedTask", this._id);
//console.log(this._id);
//console.log(goal._id);
console.log(task._id);
//console.log('Click event happened: this._id saved as selectedItem Session variable.');
}
});
When I click on a task I receive this error on the console: "undefined" and I don't really understand the reason behind. I did some research and found a possible solution: Maybe 'click .task': function(task) should receive the task context or input so it could be able to grasp the meaning of this._id.
I have a {{#afModal doc=task._id}} which also should receive the value of task._id and does not seem to work, although it is placed in the right context I think.
I have a feeling that the two issues are related somehow.
The problem is that the {{#each goal in goals}} loop syntax does not change the data context within the loop (see docs). It simply adds a goal variable for use in your spacebars template.
One solution would be to move the contents of the {{#each task in ...}} loop to another template like so.
<template name="task">
{{#each goal in goals}}
{{#each task in relatedTasks goal}}
{{> goalTask goal=goal task=task}}
{{/each}}
{{/each}}
</template>
<template name="goalTask">
<li>
<span class="text task">
<strong>{{task.taskName}}</strong>
to {{goal.goalName}}<br>
taskid: {{task._id}} {{task.taskPostpone}}
</span>
{{#afModal class="btn btn-primary" collection="Tasks" operation="update" doc=task._id}}
Update {{task.taskName}}
{{/afModal}}
</li>
</template>
Your event handler would then look like this.
Template.goalTask.events({
'click .task': function() {
console.log(this.goal._id);
console.log(this.task._id);
}
});
This is a common problem with events in nested objects, how to get the data context of the object that was clicked on?
The simplest way to approach this problem is to create a template for each level of nesting. The proper context is then provided automatically.
<template name="goals">
{{#each goals}}
{{#each task}}
{{> task}}
{{/each}}
{{/each}}
</template>
<template name="task">
<li>
<span class="text task"><a href="#modal-taskedit" data-toggle="modal">
<strong>{{task.taskName}}</strong></a> to {{goal.goalName}}<br>
taskid: {{task._id}}{{task.taskPostpone}}</span>
{{#afModal class="btn btn-primary" collection="Tasks" operation="update" doc=_id}}
Update {{task.taskName}}
{{/afModal}}
</li>
</template>
Then your event handler can be defined on the inner task template with the data context automatically being the individual task and not the data context of the outer template.
Template.task.events({
'click .task': function(){
Session.set("selectedTask", this._id);
}
});

Nested Meteor (Blaze) templates controlled by parent

I'm trying to avoid repetitive template code for forms that are essential the same when creating or editing new items.
For example, something like this:
<template name="createOrEdit">
<form role="form">
<div class="form-group">
<input type="text" class="form-control" id="title" placeholder="Title"/ value="{{title}}">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</template>
<template name="create">
{{> createOrEdit}}
</template>
<template name="edit">
{{> createOrEdit}}
</template>
Then, I could created separate template handlers:
Template.create.events(...
Template.edit.events(...
However, those parent template wrappers won't get the events for the main child template.
Is there a way to do what I want?
Those parent templates can get the events from child template. Use it like this:
Template.create.events({
'click .btn':function(){
}
})
Template.edit.events({
'click .btn':function(){
}
})
In Template.createOrEdit.events object you keep events used by both templates and in Template.edit.events and Template.create.events specific code for each.
see proof and
source code
This approach is really nice as you can customize form by passing some variables:
{{# create btnText="create" }}{{/create}}
{{# edit btnText="update" }}{{/edit}}
And in createOrEdit template you can use variable btnText to change button's label.

Meteor js not responding

I have been working with meteor.js and have been practicing using examples from getting started with meteor.js Javascript framework. The book is 2 years old and I have been running across some snags. For instance the book tells you to use var to define a variable, but after searching on stack I read that you didn't have to use it and now it works. I'm new so I write programs, run them, debug them and start from scratch to help me learn. For some reason this program that I have done 4 times before today is not running and I cant figure out why.
I keep getting this message :
While building the application:
LendLib.html:37: Expected "template" end tag
...  </div>
after inputting the following code:
<head>
<title>LendLib</title>
</head>
<body>
{{> hello}}
<div id="categories-container">
{{> categories}}
</div>
</body>
<template name="hello">
<h1>Lending Library</h1>{{greeting}}
<input type="button" value="Click" />
<template name="categories">
<div class="title">my stuff</div>
<div id="categories">{{#each lists}}
<div class="category">{{Category}}</div>{{/each}} </div>
</template>
</template>
any advice will be appreciated
dont define templates inside another template.
try like this.
<template name="hello">
<h1>Lending Library</h1>
{{greeting}}
<input type="button" value="Click" />
</template>
<template name="categories">
<div class="title">my stuff</div>
<div id="categories">
{{#each lists}}
<div class="category">
{{Category}}
</div>
{{/each}} 
</div>
</template>
Like pahan said, you cannot define a template within another template. They each have to be separate and un-nested. You CAN, however, call a template from within another template.
<template name="myOtherTemplate">
<h1> Lalalala </h1>
</template>
<template name="myTemplate">
{{> myOtherTemplate}} //injects the contents of myOtherTemplate
</template>
On another note, you also cannot nest Template instances within other Template instances.
Say that you want to register a helper function only after a certain template has been rendered.
Template.myTemplate.rendered = function({
Template.myTemplate.helpers({
key: "value",
anotherKey: "anotherValue"
});
});
^^ This also won't work.

re-rendering template when changing property from click event

i would like to trigger the visibility of a block in my handlebars template from an onclick function. i can use sessions and a helper function to get this to work but it seems overkill.
<template name="eventlist">
{{#each eventcollection}}
<div class="eventcontent">
name: {{name}} <br>
{{#if showdetail}}
detail: {{detail}}
{{/if}}
</div>
{{/each}}
</template>
could it be possible to make it work somehow like this?
Template.eventlist.events = {
'click .eventcontent': function() { this.showdetail = true}
}
meteor would just need to check if any attribute of this changed after the event completed and then rerender the template
Why bother Meteor? Such things were easy to achieve in the ancient Javascript days:
<template name="eventList">
{{#each eventCollection}}
<div class="eventContent">
name ...
<span class="eventHidden" style="display: none;">
detail ...
</span>
</div>
{{/each}}
</template>
Template.eventList.events({
'click .eventContent': function(e) {
$(e.target).find('.eventHidden').toggle();
},
});

Do I need to Reload the Site in Meteor?

I have this Template:
<template name="body">
{{#if key}}
{{> mite}}
{{else}}
{{> settings}}
{{/if}}
</template>
and
<template name="settings">
<h1>The settings</h1>
<form class="form-inline">
<input id='apiKey' type='text' name='apiKey' placeholder='your API-Key'>
<button id='saveSettings' type='submit' class='btn'>save</button>
</form>
</template>
<template name="mite">
<div>
<h3>...here with key</h3>
<p>
<a id="optout" href="#">not your key?</a>
</p>
</div>
</template>
When I show the settings-form where the user can set the key needed to show the 'mite' template. Now when i 'submit' the form the page get reloaded and the 'mite' template is shown.
On the mite template I'd like to have that link 'not your key?' or something that deletes the key and then shows the settings-form again. It works with a reload... but can't I do this without all the reloading in Meteor? How can i 'call' the template part with the #if in the body template?
-- Renato
You need to bind an event handler to your form and use preventDefault() to stop it submitting. e.g
client side js
Template.settings.events({
'submit':function(event,template) {
event.preventDefault();
var apiKey = template.find('input[name=apiKey]').value;
//..rest of logic to handle submit event
Session.set("key",true);
}
});
You can then use a template helper with Session.get("showthistemplate") to decide whether to show another template or not: (this is a universal helper since you're putting it in and not a template:
Handlebars.registerHelper('key',function() {
return Session.set("key",true);
});

Resources