I\'ve been creating a htmlHelper function using TypeScript and KnockoutJS to edit a list of emails.
The list of emails is a Knockout ObservableArray called e
to add my 2 cents, there's also a dirty way, that leverages the variable _this created by the Typescript compiler to keep a reference on this :
public deleteItem(emailToDelete: string) {
var that = eval('_this');
// remove item from list
that.emails.remove(emailToDelete); // remove? in JS, really?
}
declare class Email { }
declare class ObservableArray {
remove(any): void;
}
class MyViewModel {
public emails : ObservableArray;
constructor() {
Rebind(this);
}
public deleteItem(emailToDelete: Email) {
this.emails.remove(emailToDelete);
}
}
function Rebind(obj : any)
{
var prototype = <Object>obj.constructor.prototype;
for (var name in prototype) {
if (!obj.hasOwnProperty(name)
&& typeof prototype[name] === "function") {
var method = <Function>prototype[name];
obj[name] = method.bind(obj);
}
}
}
You might want a polyfill for Function.bind()
:
// Polyfill for Function.bind(). Slightly modified version of
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
if (typeof Function.prototype.bind !== "function") {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = <any[]> Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP && oThis ? this: oThis, aArgs.concat());
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
My final solution is a base class, that rebinds all prototype functions to itself on constructor. Much like Markus Jarderot's solution.
class BaseClass {
constructor() {
for (var i in this) {
if (!this.hasOwnProperty(i) && typeof (this[i]) === 'function' && i != 'constructor') {
this[i] = this[i].bind(this);
}
}
}
}
Advantages:
PS:
You still will need the bind polyfill.
I'm using typesript 0.9.5
Use data-bind something like this:
data-bind="click:$parent.deleteItem.bind($parent)"
Assign this
to that
as shown below
public deleteItem(itemToDelete)
{
var that = this;
// remove item from list
that.emails.remove(itemToDelete);
}
Gloves people! Just bind $parent as this:
<a href="#" data-bind="click: $parent.deleteItem.bind($parent)">Delete</a>
Although I prefer Markus' solution, here's what I have used before to work around this issue:
public fixThis(_this, func) {
return function () {
return _this[func].apply(_this, arguments);
};
}
<a href="#" data-bind="click: fixThis($parent, 'deleteItem')">Delete</a>
Note that additional arguments can be passed to the method by adding them after the name of the method:
fixThis($parent, 'deleteItem', arg1, arg2);