What is "this" in Meteor's "Template.myTemplate.rendered" callback? - meteor

According to the docs.meteor, in the body of the "Template.myTemplate.rendered" callback, "this" is a template instance object.
However, when I insert a "debugger" line in the callback and use the browser dev tools to check, the value of "this" is "window". Am I doing something wrong?
I'm using the Leaderboard example - here's the handlebars template:
<template name="leaderboard">
{{#each players}}
{{> player}}
{{/each}}
{{#if selected_name}}
<div class="details">
<div class="name">{{selected_name}}</div>
<input type="button" class="inc" value="Give 5 points" />
<input type="button" class="fastclick inc" value="Give 5 points - fast" />
</div>
{{/if}}
{{#unless selected_name}}
<div class="none">Click a player to select</div>
{{/unless}}
</template>
<template name="player">
<div class="player {{selected}} fastclick">
<span class="name">{{name}}</span>
<span class="score">{{score}}</span>
</div>
</template>
And the "Template.leaderboard.rendered" callback:
Template.leaderboard.rendered = function (){
Meteor.defer(function() {
debugger;
new FastClick(document.body);
console.log("Template.leaderboard.rendered: " + JSON.stringify(this));
});
}
}

I think your problem is that this is inside the Meteor.defer callback, which means that the context of this has changed. Try caching this in a variable, and then outputting that variable in console.log(). For example:
Template.leaderboard.rendered = function (){
var self = this;
Meteor.defer(function() {
debugger;
new FastClick(document.body);
console.log("Template.leaderboard.rendered: " + self );
});
}
}

In the body of the callback, this is a template instance object that is unique to this occurrence of the template and persists across re-renderings. Use the created and destroyed callbacks to perform initialization or clean-up on the object.
Source: http://docs.meteor.com/#template_rendered
So this references the particular instance of your template that you are rendering.

Related

How to pass data variable from each to the with in Meteor?

I am trying to pass git variable to settings which is wrapping with with as shown below
If we can see right now it is setting.gitlab but i want to make it dynamically like setting.git where git is a variable mentioned in each loop .
{{#each git in gitlabFields}}
{{#with settings.gitlab}}
<div data-value={{#index}}>{{git}}</div>
<div>hihi</div>
<div class="rc-user-info__row">
<div class="rc-input">
<label class="rc-input__label">
<div >
<div class="rc-input__title" style="display: inline-block;" >{{_ label}}{{equal default value '*'}}</div>
</div>
<!-- {{#each gitlabFields}} -->
<div id="dynamicFields">
<div class="rc-input__wrapper" >
<input type="text" name="{{git}}" value="{{value}}" class="rc-input__element js-input" disabled="{{./disabled}}"/>
</div>
</div>
<!-- {{/each}} -->
</label>
</div>
</div>
{{/with}}
{{/each}}
git variable is not accessible here settings.git
Its showing undefined .
Assuming settings is an accessible Object, you can write a helper, that resolves the value:
Template.myTemplate.helpers({
getGitSettings (settings, key) {
return settings[key]
}
})
If you want to decouple settings from the template or avoid passing it through the whole display list you can also define it within the Template module as private variable:
const gitSettings = { ... };
Template.myTemplate.helpers({
getGitSettings (settings, key) {
return gitSettings[key]
}
})
If this pattern is used among many Templates you can also define a global helper:
const gitSettings = { ... };
Template.registerHelper('gitSettings', function (key) {
return gitSettings[key]
})
and use it via
{{#each field in gitlabFields}}
{{#with gitSettings field}}...{{/with}}
{{/each}}

This.data from #each-iteration

I'm trying to access a value inside an {{#each in}}-iteration:
{{#each room in channels}}
<form class="enterRoom">
<button type="submit" class="roomJoin">
<b>{{room.name}}</b>
<img src="{{room.roomBanner}}" alt=".">
<input type="hidden" value="{{room.name}}" name="name">
</button>
<div class="inRoom">
{{#each name in room.inRoom}}
{{name}}
{{/each}}
</div>
</form>
{{/each}}
Normally I would use this.name, for example, to get the name of it inside an event to use it further, like so
'submit .enterRoom'(event) {
event.preventDefault();
const isClosed = this.name; // room.name example here
}
But this doesn't work in this scenario. What I tried before was:
room.name
this.room.name
But those give the same error
chat.js:86 Uncaught ReferenceError: room is not defined
at Object.submit .enterRoom (chat.js:86)
at blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3818
at Function.Template._withTemplateInstanceFunc (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3769)
at Blaze.View.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3817)
at blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2617
at Object.Blaze._withCurrentView (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2271)
at Blaze._DOMRange.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2616)
at HTMLFormElement.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:863)
at HTMLDivElement.dispatch (modules.js?hash=8331598f8baf48556a442a64933e9b70b778274a:9685)
at HTMLDivElement.elemData.handle (modules.js?hash=8331598f8baf48556a442a64933e9b70b778274a:9492)
Could someone explain to me how I could do it in this {{each in}}-setting properly?
The error has nothing to do with the each iterations of your template. What you try is to get the form data within the submit event handle. However, there is no context bound to this or room.
In order to get the room value, you need to access the input value.
Blaze offers a fast way of doing so, by using the Template's builtin jQuery (using templateInstance.$), which automatically scopes to the Template root instead of the whole document:
'submit .enterRoom'(event, templateInstance) {
event.preventDefault();
const roomName = templateInstance.$(event.currentTarget).find('input[name="name"]').val();
// ...
}

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

Template reuse in meteor

I'm trying to reuse some control elements in my Meteor app. I'd like the following two templates to toggle visibility and submission of different forms.
<template name='addControl'>
<img class='add' src='/images/icon-plus.png' />
</template>
<template name='okCancelControl'>
<img class='submit' src='/images/icon-submit.png' />
<img class='cancel' src='/images/icon-cancel.png' />
</template>
I'll call these templates in another:
<template name='insectForm'>
{{#if editing}}
<!-- form elements -->
{{> okCancelControl}}
{{else}}
{{> addControl}}
{{/if}}
</template>
editing is a Session boolean.
What's a good way to wire up the controls to show, hide and "submit" the form?
The main problem is finding the addInsect template (where the data is) from the control templates (where the "submit" event fires). Here's what I did:
First, the controls:
<template name='addControl'>
<section class='controls'>
<span class="add icon-plus"></span>
</section>
</template>
<template name='okCancelControl'>
<section class='controls'>
<span class="submit icon-publish"></span>
<span class="cancel icon-cancel"></span>
</section>
</template>
Now the javascripts. They simply invoke a callback when clicked.
Template.addControl.events({
'click .add': function(event, template) {
if (this.add != null) {
this.add(event, template);
}
}
});
Template.okCancelControl.events({
'click .cancel': function(event, template) {
if (this.cancel != null) {
this.cancel(event, template);
}
},
'click .submit': function(event, template) {
if (this.submit != null) {
this.submit(event, template);
}
}
});
I then connected the callbacks using handlebars' #with block helper.
<template name='addInsect'>
{{#with controlCallbacks}}
{{#if addingInsect}}
<section class='form'>
{{> insectErrors}}
<label for='scientificName'>Scientific Name <input type='text' id='scientificName' /></label>
<label for='commonName'>Common Name <input type='text' id='commonName' /></label>
{{> okCancelControl}}
</section>
{{else}}
{{> addControl}}
{{/if}}
{{/with}}
</template>
And the corresponding javascript that creates the callbacks relevant to this form.
Session.set('addingInsect', false);
Template.addInsect.controlCallbacks = {
add: function() {
Session.set('addingInsect', true);
Session.set('addInsectErrors', null);
},
cancel: function() {
Session.set('addingInsect', false);
Session.set('addInsectErrors', null);
},
submit: function() {
var attrs, errors;
attrs = {
commonName: DomUtils.find(document, 'input#commonName').value,
scientificName: DomUtils.find(document, 'input#scientificName').value
};
errors = Insects.validate(attrs);
Session.set('addInsectErrors', errors);
if (errors == null) {
Session.set('addingInsect', false);
Meteor.call('newInsect', attrs);
}
}
};
Template.addInsect.addingInsect = function() {
Session.get('addingInsect');
};
Template.addInsect.events = {
'keyup #scientificName, keyup #commonName': function(event, template) {
if (event.which === 13) {
this.submit();
}
}
};
In the submit callback I had to use DomUtils.find rather than template.find because template is an instance of okCancelControl, not addInsect.
You can use Session for this. You Just need a template helper that returns a boolean flag that indicates whether you are editing the form fields. And manipulate the DOM based on the Session value set by this template helper.
Assume you have one text input, now when you are entering text in it, set the Session flag as true. This will trigger the helper to return true flag, Based on that, one of your two templates will be rendered in the DOM.
The isEditing is the helper that triggers whenever you change the Session value.
This helper function is the main part here, it returns true/false based on the session value you have set.
Template.insectForm.isEditing = function(){
if(Session.get('isEditing')){
return true;
}
else{
return false;
}
}
Remember to set the Session to false at the start-up as:
$(document).ready(function(){
Session.set('isEditing', false);
})
This will render the default add template in the html, Now when you click on ADD, you need to display another template, for that, set Session to true as:
'click .add' : function(){
Session.set('isEditing', true);
}
Accordingly when you click on CANCEL, set the session to false, this will make the isEditing to return false and the default add template will be displayed.
So your complete html will look something like this:
<template name='insectForm'>
{{#if isEditing}}
<!-- form elements -->
<input type="text" id="text" value="">
{{> okCancelControl}}
{{else}}
{{> addControl}}
{{/if}}
</template>
<template name='addControl'>
<img class='add' src='/images/icon-plus.png' />
</template>
<template name='okCancelControl'>
<img class='submit' src='/images/icon-submit.png' />
<img class='cancel' src='/images/icon-cancel.png' />
</template>
[UPDATE]
To get the instance of the template, you'll need to pass the additional parameter in the event handler that represents the template.
So update your event handler as:
Template.insectForm.events = {
'click .submit' : function(event, template){
//your event handling code
}
}
The parameter template is the instance of the template from which the event originates.
Note that, although the event fires form the image that is inside the okCancelControl template, the parameter will still contain the instance of the insectForm template. This is because we are calling the event handler as Template.insectForm.events = {} .
Also see this answer for template instances.

Getting the outer context of a Meteor Template

I have the following use-case: There are rooms which have beds inside. (Bummer...)
There is a loop of rooms which uses a template "room".
<template name="rooms">
{{#each availableRooms}}
{{> room}}
{{/each}}
</template>
This template gets for each iteration a room. This is accessible by this.
<template name="room">
<div class="room-outer">
<button type="button" class="btn" data-toggle="collapse" data-target="#list-{{_id}}">
{{name}} : {{getBeds this}} beds free.
</button>
<div id="list-{{_id}}" class="collapse in room-inner">
{{#each guests_id}}
<div class="bed">
<div class="blanket">
{{showUser this}}
</div>
</div>
{{/each}}
</div>
</div>
</template>
Now I like to calculate some special value which I do by extending the template. I need now to pass the this variable to the getBeds function. Is it possible to do this by grabing outside the template and get the room into the function?
Template.room.getBeds = function (room) {
if (room.guests_id)
return room.beds - _.size(room.guests_id);
else
return room.beds;
};
Basically I don't want to have to write {{getBeds this}} but only {{getBeds}}
Shouldn't this work?
Template.room.getBeds = function () {
if (this.guests_id)
return this.beds - _.size(this.guests_id);
else
return this.beds;
};
See the docs:
Helpers can take arguments, and they receive the current template data in this:

Resources