Reputation: 14990
I'm going through the learn.knockoutjs.com tutorial and I'm at step 4 of the custom bindings tutorial. There is the following JavaScript:
update: function(element, valueAccessor) {
// Give the first x stars the "chosen" class, where x <= rating
var observable = valueAccessor();
$("span", element).each(function(index) {
$(this).toggleClass("chosen", index < observable());
});
}
I have translated it to the following TypeScript:
update: (element, valueAccessor) => {
var observable = valueAccessor();
$("span", element).each(index => {
$(this).toggleClass('chosen', index < observable())
});
}
This creates the following, where a _this variable is created to preserve the scope of the "update" function, rather than the internal "span" function.
update: function (element, valueAccessor) {
var _this = this;
var observable = valueAccessor();
$("span", element).each(function (index) {
$(_this).toggleClass('chosen', index < observable());
});
}
The problem is with $(_this)
. How do I get TypeScript to give me the real $(this)
.
Upvotes: 1
Views: 928
Reputation: 140230
Well the typescript doing _this
is the more intuitive one for many, in fact code like this causes multiple duplicates per day on Stackoverflow:
update: function() {
$(elem).click(function(){
this.save();
});
}
this
is not the same this
because in js this
works differently from other languages - it has a separate binding for every function call.
Typescript would make the above work as expected by many newcomers to js:
update: function() {
//this is the object with update, save methods etc
$(elem).click( () => {
this.save(); //this is still the object with update, save methods etc
});
}
There is usually a way to refer to what you want even when using fat arrow:
update: function() {
$(elem).click( (event) => {
$(event.currentTarget).hide() //same as $(this).hide() with normal function
this.save(); //Stays intuitive to non-javascripters
});
}
Or with your example:
update: function() {
$("span", element).each( (index, elem) => {
$(elem).toggleClass('chosen', index < observable())
this.save(); //stays intuitive to non-javascripters
});
}
Of course for experienced Javascripters that are used to the JS-behavior of this
, it might not be so intuitive.
Btw I am not 100% on the typescript syntax...
Upvotes: 3
Reputation: 28850
Esailija has the right idea here: don't use $(this)
in the callback at all. Your code will be much cleaner with a named parameter.
As another example of how that helps, consider this simple jQuery plugin:
// Set a random opacity on each of the elements in a jQuery object
jQuery.fn.randomOpacity = function() {
return this.each( function() {
$(this).css({ opacity: Math.random() });
});
};
Ouch. Can a piece of code possibly get any more confusing than that? this
appears on two lines in a row, and it means completely different things.
Instead, use the named parameter:
// Set a random opacity on each of the elements in a jQuery object
jQuery.fn.randomOpacity = function() {
return this.each( function( i, element ) {
$(element).css({ opacity: Math.random() });
});
};
That is much more clear.
Upvotes: 1
Reputation: 14990
The answer is very simple. Do not use a lambda.
update: (element, valueAccessor) => {
var observable = valueAccessor();
$("span", element).each(function (index) {
$(this).toggleClass('chosen', index < observable())
});
}
Note that the difference is that this code uses ... element).each(function (index)...
instead of the lambda
I do not understand why so if someone could post why, that'd be great.
Upvotes: 1