re-rendering template when changing property from click event - meteor

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

Related

Meteor - event on {{if currentUser}} content loaded

I have a template
{{#if currentUser}}
<input id="datetimepicker" type="text" >
{{/if}}
I want do add
$('#datetimepicker').datetimepicker();
But in methods of template:
onCreated
onRendered
content of {{#if currentUser}} is not accessible because collection with user is loaded after template. I can use setTimeout, but this is non stable solution. I can to type in template
{{#if currentUser}}
<input id="datetimepicker" type="text" >
<script>
$('#datetimepicker').datetimepicker();
</script>
{{/if}}
but this is not elegant.
How to catch rendering of content in block {{if currentUser}} in correct way? Or maybe should I not use this syntax generally and there is other manner of checking is user is loaded. If yes, link to proper tutorial please.
The way to do this is to make the content of the if another template, and then use the onRendered or onCreated methods of that template.
{{#if currentUser}}
{{> datePicker}}
{{/if}}
...
<template name="datePicker">
<input id="datetimepicker" type="text" >
</template>
JS:
Template.datePicker.onCreated(() => {
// something
});
#Christian Fritz's answer works well.
In case you don't want to create a new template because of some other issues, you can also try this in the onRendered callback:
Tracker.autorun(function() {
if ($('#datetimepicker')[0]) {
$('#datetimepicker').datetimepicker();
}
});

Meteor: template helpers firing on page back

I have a helper for a 'userLayout' template, in which I've defined a current user function. It works fine when I navigate to the template (i'm using flow router with subscriptions in the route). However, when I press 'back' in the browser, the helper fires again.
Why is the helper firing for the template I'm leaving when I press 'back'?
My code for one of the offending helpers is here - I suspect the 'context' attribute:
// Return a specific user
findUser: function(context) {
var id = context.user;
return Potential_users.find({_id: id});
}
EDIT
The view template itself looks like this:
<template name="userLayoutView">
{{#each findUser context}}
...
{{/each}}
</template>
And that view is getting called dynamically like so:
<template name="userLayout">
<div class="row">
<div class="col-xs-12">
{{#if currentUser context}}
{{> userLayoutEditCreate context=context}}
{{else}}
{{> userLayoutView context=context}}
{{/if}}
</div>
</div>
</template>

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

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

Can Meteor child templates access parent template helpers?

Say we have a parent template and a child template:
<template name="parent">
{{> child }}
</template>
<template name="child">
{{#if show}}
//Do something
{{/if}}
</template>
If we assign 'show' to the parent template:
if (Meteor.isClient){
Template.parent.show = function(){
return Session.get('isShowing');
}
}
Is there any way for the child template to have access to it?
Edit
You could make a universal handlebars helper so you could use Sessions values anywhere in your html:
Client js
Handlebars.registerHelper('session', function(key) {
return Session.get(key);
});
Client HTML
<template name="child">
{{#if session "show"}}
//Do something
{{/if}}
</template>
Similarly, you could also use {{session "show"}} / {{#if session "show"}} in your parent template and not have to use the Template.parent.show helper anymore.
Regarding the use of ../ notation. There are certain scenarios it may not work: https://github.com/meteor/meteor/issues/563. Basically it works within {{#block helpers}} but not with templates, but it would work in a block helper if it contains a subtemplate.
<template name="child">
{{#if ../show}}
Do something
{{/if}}
</template>
You can also register a common helper :
Template.registerHelper('isTrue', function(boolean) {
return boolean == "true";
});
And call it just like that in your html:
<input type="checkbox" checked="{{isTrue attr}}"/>

Resources