"this" value in template helper returns Window - meteor

I have the following helper
Template.meetingRow.helpers({
isOwner: () => {
console.log(this);
return Meteor.userId() === this.owner;
}
});
which will log the Window object to the console.
The this object returns the correct object in Template.meetingRow.events, and uses the correct values in the template, but isn't correct in the helper. Am I misunderstanding something?
Here's how it's being instantiated in its parent template:
<ul class="list-group">
{{#each meetings}}
{{ > meetingRow }}
{{/each}}
</ul>

It's because in ES2015, arrow functions inherit the context of the surrounding function, rather than being given their own. What this means is that Meteor can't bind a context to your helper function if it's defined this way.
Solution (in ES2015 style):
Template.meetingRow.helpers({
isOwner() function {
console.log(this);
return Meteor.userId() === this.owner;
}
});
More here under "Lexical this".

Related

Meteor - Reloading template section after variable change

i want to refresh/reload a part of my template after a variable change so that if the variable is true it shows a content A or else it will show content B. I'm sure this is a quite simple question but i'm having troubles on finding the solution.
Something like this:
Template.x.created = function() {
this.variable = false;
}
Template.x.helpers({
'getValue': function(){
return this.variable;
}
});
Template:
<template name="x">
{{#if getValue}}
<content A>
{{else}}
<content B>
{{/if}}
</template>
You need to create a reactive data source to get the template helper to re-run when the variable changes, as a normal variable won't let the helper know when it changes value. The simplest solution is to use ReactiveVar:
Template.x.onCreated(function() {
this.variable = new ReactiveVar(false);
});
Template.x.helpers({
'getValue': function() {
// Note that 'this' inside a template helper may not refer to the template instance
return Template.instance().variable.get();
}
});
If you need to access the value somewhere outside this template, you can use Session as an alternative reactive data source.
#Waiski answer is a good one, but I want to share a simple Template helper I build because a lot of Templates need this:
Using registerHelper you can build a global helper like so:
Template.registerHelper('get', function (key) {
let obj = Template.instance()[key]
return (obj && obj.get) ? obj.get() : obj
})
Use it in every template:
Template.x.onCreated(function() {
this.foo = new ReactiveVar(true)
this.bar = new ReactiveVar('abc')
})
Html:
{{#let foo=(get 'foo')}}
{{#if get 'bar'}}
Bar is true. Foo: {{foo}}
{{/if}}
{{/let}}

Meteor {{#if}} helper if object or field exists

I am looping through documents in a template with Blaze spacebars to create a list
<template name="objectTemplate">
{{#if checkIfObjectExists}}
({{document.[0].object.object1}})
{{/if}}
</template>
I know that in some documents, some objects do not exist in that object position. normally if I didnt have (), it would be blank and I could move on, but in this case when empty, I will have a lot of () which is not good.
I created a helper, but its not working. I have tried null, 0, typeOf etc and still cant get it right. Anyhow here is the helper
Template.objectTemplate.helper ({
checkIfObjectExists: function() {
if (this !== 'null') {
return true;
} else {
return false;
}
}
});`
You can use _.has(object, key) if you want to check if the object document.[0].object has the property object1 set. The function _.isObject(value) will check instead if document.[0].object.object1 is an Object (this also includes arrays).
So, depending on your requirements, your template helpers should look like this:
Template.objectTemplate.helper({
checkIfObjectPropertyExists: function() {
return _.has(this.document[0].object, "object1");
},
checkIfPropertyIsObject: function() {
return _.isObject(this.document.[0].object.object1);
}
});
You could also register an Underscore.js global template helper and then use it directly in your Meteor templates:
Template.registerHelper('_', function () {
return _;
});
<template name="objectTemplate">
{{#if _.has this.document.[0].object 'object1'}}
({{document.[0].object.object1}})
{{/if}}
</template>
Your if is not in the right place. Your objectTemplate is probably called that way :
{{#each datum in data}}
{{>objectTemplate data=data}}
{{/each}}
So it's always rendered. Even if the datum is empty. The this you check in your helper will always be true, it's the template himself.
So, you should call it that way :
{{#each datum in data}}
{{#if datum.thingToTest}}
{{>objectTemplate datum=datum}}
{{/if}}
{{/each}}
The entire sub template won't be called.

Why this works: Meteor-Blaze #with and data context?

I wonder why the following thing outputs 'hello' instead of 'bye'???
Template:
<template name="example">
{{#with dataContext}}
{{say}}
{{/with}}
</template>
Template Helper:
Template.example.helpers({
dataContext: function() {
return {
say: 'bye'
};
},
say: function() {
return 'hello';
}
});
(Meteor 1.1.0.2)
The shortest answer to this is the helpers have a preference over the data context.
If you rename one of them to something else it should solve your problem.
The order the lookup goes is:
The data context (if it contains a .). {{say}} does not.
The template's helper. {{say}} has a helper for say.
A template
A global helper such as those defined with Template.registerHelper.
The data context
So if the first isn't found, it goes down the list until it finds something
[1]https://github.com/meteor/meteor/blob/90b356061ff2464f11749dc8b43d1a139b233980/packages/blaze/lookup.js#L100-L139

Why is 'this' in a Meteor Template onRendered Function undefined?

I'm using meteor 0.6.4. The problem i have is that the data context when rendering a template is sometimes undefined, so that, 'this' object is a reference to Window:
Template.task.time_left = function(){
debugger;
var nDate = this.due_date.getTime();
Exception from Deps recompute: TypeError: Cannot call method 'getTime' of undefined
The html code is wrapped inside an {{each}} handlebars statement:
<template name="tasks_lists">
{{#each tasks_list}}
...
{{#each task}}
{{> task}}
{{/each}}
...
{{/each}}
</template>
<template name="task">
...
<div class="text">{{due_date}}</div>
...
</template>
I read that this bug was solved in an earlier version of Meteor. What can I do to aviod the function being called with 'this' as Window.
You should use template.xxx.helpers instead, ie:
Template.task.helpers({
nDate: function() {
return this.due_date.getTime();
}
});
When you use it within helpers, this is the data context.
I used 'helpers' function and I have the same problem. 'this' object is sometimes the window object:
Template.task.helpers({
...
'time_left': function(){
debugger;
var nDate = this.due_date.getTime();
...
When using any of the three Meteor template callback functions, including the onRendered function, the this object is actually a template instance object. Although it is possible to retrieve the data context of the template through this object, it is recommended that you reference the template data context through the use of the Template.currentData() function. The documentation for this function can be found here.
The this inside a template helper will always points to window object.
You can access the data context in Template.rendered() or the event handler function. In the event handlers, it is passed as a second argument as function( event, template ), where the template is the current template object.
However I suggest you to use the template instance functions such as find(), findAll(), firstNode(), lastNode() rather than data context.
Template.task.rendered = function() {
if( !this.window ){ //check that 'this' is not a 'window' object
var el = this.find( 'div.text' ); // the div that holds due_date
//do something
}
}

in Meteor, how do i update a property on only one instance of a template?

If I have an {{# each}} binding in Meteor, and I want to update a property on only one instance of the template inside the #each. How would I do that? I've tried setting a value on the "template" object inside the events map, but that doesn't seem to be reactive. I've also tried binding to a Session property, but that will cause every instance to update instead of just the one I want...
for example:
{{#each dates}}
{{> dateTemplate}}
{{/each}}
<template name="dateTemplate">
{{date}}
<span style="color: red;">{{errorMsg}}</span> <--- how do i update errorMsg?
</template>
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = 'not valid'; <--- this doesn't do anything
}
});
EDIT TO ADDRESS ANSWER BELOW:
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid';} <--- this also doesn't do anything
}
});
You don't have to use handlebars for this, because its not something that needs reactivity to pass the message through, reactive variables work best with db data, or data that would be updated by another client over the air.
You could use JQuery (included by default) to update it, it can also get a bit fancier:
<template name="dateTemplate">
{{date}}
<span style="color: red;display: none" class="errorMessage"></span>
</template>
Template.dateTemplate.events({
'click': function(event, template) {
$(template.find('.errorMessage')).html('Your Error Message').slideDown();
}
});
Ive edited it so the error is hidden by default, and slides down with an animation
I'm experimenting handling this by passing a different reactive object to each instance of the template. Then the template can bind to the reactive object (which is unique per instance) and we don't have any extra boilerplate.
It ends up looking like this:
Initial render:
Template.firstTemplateWithPoll(ContextProvider.getContext())
Template.secondTemplateWithPoll(ContextProvider.getContext())
// (I actually pass getContext an identifier so I always get the same context for the same template)
JS:
Template.poll.events = {
'click .yes' : function() {
this.reactive.set('selection', 'yes');
},
'click .no' : function() {
this.reactive.set('selection', 'no');
}
};
Template.poll.selection = function(arg) {
return this.reactive.get('selection');
}
Template:
<template name="poll">
<blockquote>
<p>
Your selection on this poll is {{selection}}
</p>
</blockquote>
<button class='yes'>YES</button>
<button class='no'>NO</button>
</template>
template.errorMsg should be a function that returns your error.
Template.dateTemplate.events({
'click': function(event, template) {
template.errorMsg = function() { return 'not valid'; };
}
});

Resources