Losing this reference in $scope.$on event - javascript

I'm registering the "$routeChangeSuccessEvent" from Angularjs by setting the callback function. When the event is raised I can not access my controllers instance by "this". The current this instance is unedfined.
My complete TypeScript Code:
export class Ctlr {
static $inject = ["$rootScope","$route"];
constructor(private $scope: ng.IRootScopeService) {
this.Scope = $scope;
this.Title = "";
//this.Scope.$on("$routeChangeSuccessEvent", this.onRouteChangeStart);
this.RegisterEvents();
}
private RegisterEvents(): void {
this.Scope.$on("$routeChangeSuccessEvent",(event: ng.IAngularEvent, args: any) => {
//this is undefined
console.log(this);
});
}
public Scope: ng.IScope;
public Title: string;
public onRouteChangeStart(event: ng.IAngularEvent, args: any) {
//this is undefined
this.Title = args.$$route.name);
}
}
}
I'm able to get access of the Title property with:
private RegisterEvents(): void {
var ref = this.Title;
this.Scope.$on("$routeChangeSuccessEvent",(event: ng.IAngularEvent, args: any) => {
ref = args.$$route.name;
});
}
But that's not a real solution because angularJS doesn't update its view. It seems that I didn't catch the right reference. If thats not possible the whole angularjs events seems to be not useabel - that can't be possible?
I also didn't find any topic about this strange behavior. Is there a solution for this issue?

The scope changes when a callback is fired, that is why this becomes undefined.
Your other example of doing:
var ref = this.Title;
Actually just creates a -copy- of Title as it is a primitive type (string). Which is why it didn't work either. Updating ref does not update this.Title.
The usual solution to this, is to start the definition as:
var vm = this;
...
private RegisterEvents(): void {
this.Scope.$on("$routeChangeSuccessEvent",(event: ng.IAngularEvent, args: any) => {
//this is undefined
console.log(vm);
});
}
So rather than using this everywhere, you would use vm. Note that vm can be named whatever you want. The important part is you capture a reference to this in a scope where this is what you want to use in a callback. This works because this is not a primitive type, as it is an object and rather than taking a copy, it takes a reference.
Your other option is to use bind which you can apply to any function, this function essentially tells JavaScript what this will equate to. E.g.
$scope.$on("SomeEventHere", someCallbackFunction.bind(this));
It's a matter of preference which you use here but generally I see people using the var something = this; method.

That's because this always refers to it's parent which is now the function. So if you wanted you could do this:
private RegisterEvents(): void {
var ref = this;
this.Scope.$on("$routeChangeSuccessEvent",(event: ng.IAngularEvent, args: any) => {
console.log(ref);
});
}

you can rebind the this variable:
this.Scope.$on("$routeChangeSuccessEvent",this.onRouteChangeStart.bind(this));

As noted in other solutions, and using at least TypeScript v1.7, you can use this using a fat arrow like:
$scope.$on('some-event', () => { console.log('here `this` is not undefined'); });
$scope.$watch('foo.bar', (a,b) => { console.log('here too...'); }
$scope.$on('$routeChangeSuccessEvent', () => { console.log('here too...'); });
But, if you want to pass a reference to a function from the same class you have to use the .bind(this) notation:
$cope.$on('$routeChangeSuccessEvent', this.onRouteChangeStart.bind(this));

Related

Variable is always undefined when called in a function

Before I come to my question, here is my Typescript class:
export class Info {
private currentId: number;
private addToListButton: HTMLInputElement;
constructor() {
this.currentId = +(<HTMLInputElement> document.getElementById("Example")).value;
this.addToListButton = <HTMLInputElement> document.getElementById("addToList");
this.registerEvents();
}
private registerEvents(): void {
this.addToListButton.addEventListener("click", (() => this.addToList()));
}
private addToList(): void {
var data: SomeModel;
data.id = this.currentId;
// ajax stuff
}
}
My problem is when I want to get the "currentId" for the model, it's always undefined.
The other variable "addToListButton" can I use in the same function without problems.
I can't see what's the problem there.
Thanks for your help!
The keyword this in the function addToList doesn't refer to the instance of the class Info when the function is called here this.addToListButton.addEventListener("click", (() => this.addToList()));.
To have this refering to the instance, use an arrow function:
private const addToList = (): void => { // Arrow function
var data: SomeModel;
data.id = this.currentId;
// ajax stuff
}
Here are some explanations on what arrow functions are.
In the addToList() method, the keyword this refers to the event context not the Info object context.
To use in this manner, you'll need to bind the method to the class. Add this to the constructor:
this.addToList = this.addToList.bind(this);

ES6 Javascript: Calling static methods from classes with arrow functions

While this works as intended
class ClassWithStaticMethod {
static staticMethod() {
return ('staticMethod');
};
static staticMethod2() {
const yee = this.staticMethod();
return 'staticMethod2 '+yee;
};
}
console.log(ClassWithStaticMethod.staticMethod2());
//staticMethod2 staticMethod
This is,
i) have access to the staticMethod() with the class name, and
ii) this method can call another static method within the same class by using "this",
This doesn't work
class ClassWithStaticMethod {
static staticMethod = () => {
return ('staticMethod');
};
static staticMethod2 = () => {
const yee = this.staticMethod;
return 'staticMethod2 '+yee;
};
}
console.log(ClassWithStaticMethod.staticMethod2());
//staticMethod2 undefined
In the sense, that I can still access to the staticMethod() method, but I am not able to access to the other method within the first method. I get undefined, and if I use
const yee = this.staticMethod();
I get an error
error TypeError: _this.staticMethod is not a function
Arrow functions inherit their this from the outer scope rather than their this depending on their calling context. Since staticMethod2 tries to access this.staticMethod, that will only work if this refers to ClassWithStaticMethod - that is, if staticMethod2 is a standard function, not an arrow function.
You also need to invoke this.staticMethod(). (const yee = this.staticMethod; will result in the staticMethod being coerced to a string, rather than being called)
Change those two issues, and it works as expected:
class ClassWithStaticMethod {
static staticMethod = () => {
return ('staticMethod');
};
static staticMethod2 = function() {
const yee = this.staticMethod();
return 'staticMethod2 '+yee;
};
}
console.log(ClassWithStaticMethod.staticMethod2());
That's one quirk with arrow functions when it comes to general use: they have generic scoping of this. (That's why we have to use function() if you want a better call stack). In the second method, this refers to the calling context: window.
As mentioned below in the comment, do not use the short-hand syntax for your convenience; there is a reason we have so many options.
To fix it, you can just use function() to define the second function; or () in the object case.
This, however, would work:
class ClassWithStaticMethod2 {
static staticMethod = () => {
return ('staticMethod');
};
static staticMethod2 = function() {
console.log(this)
const yee = this.staticMethod();
return 'staticMethod2 '+yee;
};
}
Check it out here.

Constructor functions in TypeScript, what is missing?

I'm trying to figure out how to use type-safety provided by TypeScript with old plain constructor functions in JS. I have a very simple example, that looks straightforward, but I miss something and can't make it compile with TypeScript:
interface IMyService {
new(): IMyService //I'm not sure if this line should be here, I just trying to make it working...
doSomething(name: string): void
}
function MyService(this: IMyService): void {
let _name = ""
this.doSomething = (name) => {
_name = name
}
}
//The line below won't compile and it saying:
//"new" expression, whose target lacks a construct signature, implicitly has an "any" type
let service = new MyService();
service.setName("Test Name")
What I'm missing? I know the preferred way of using TypeScript is with "class", but in my case I would like to use simple constructor functions.
You cant really type a function declaration (or at least i dont know how). However you can type a variable, and assign a function to it. And then we can define a constructor type:
interface IMyService {
doSomething(name: string): void;
}
interface IMyServiceConstructor {
new(): IMyService;
}
const MyService: IMyServiceConstructor = function(this: IMyService){
//...
};
That can be shortified through using an inline type:
const MyService: { new(): IMyService } = function(){
//...
};
What's stopping you from doing this:
class MyService {
// declare instance method
doSomething: (x: string) => void;
// this is really your function
constructor() {
let _name = "";
this.doSomething = (name) => {
_name = name;
}
}
}
let service = new MyService();
service.doSomething("Test Name");
This emits almost the same code as your original. It's still using a variable local to the constructor function scope, and an instance method instead of a class method. (Instance methods are generally frowned upon because you're creating closures for each instance but that's up to you.)
And TypeScript understands that MyService is newable and all the other goodness you want. Jumping through hoops with constructor type signatures and convincing TypeScript that your function is the right type doesn't seem worth it to me.
Hope that helps.

How can I add a user keyword to the VS2013 TypeScript language text editor?

I need to add "self" as a "user keyword" but I cannot see to do this in VS2013. I would like it to be a user keyword for the TypeScript editor window.
I am making frequent use of "self" in my code. Is there a way that I can add the word "self." as a keyword and have it appear in a different color in my TypeScript text editing window?
Here's an example of where I would use self and would like to have it highlighted:
class X {
abc=1;
static $inject = ['$http'];
constructor(private $http: ng.IHttpService) {}
doTask = () => {
var self = this;
this.$http({
xx
})
.success((data) => {
self.abc = data
})
}
}
Update:
I have added a few lines to the question to more closely match the way I am using self.
This will work fine with a lambda:
class X {
doTask = () => {
this.doJob({
foo: "bar"
}).success((data) => {
this.abc = data;
});
}
}
It compiles to:
function X() {
var _this = this;
this.doTask = function () {
_this.doJob({
foo: "bar"
}).success(function (data) {
_this.abc = data;
});
};
}
I recommend trying ReSharper (http://www.jetbrains.com/resharper/) to extend the syntax highlighting options in VS2013. It's a 30-day trial, but if it does what you need then I'd say it's worth purchasing. Here's a link to the documentation for the extended color highlighting:
http://www.jetbrains.com/resharper/webhelp/Coding_Assistance__Syntax_Highlighting.html
From your question, it seems that all you're interested in doing is specifying another keyword to highlight so that it visually stands out in the code. ReSharper will allow you to easily do this.
This is how would your class look like property written in TypeScript using methods. Then you would not need to bother with self.
class X {
private abc: number = 1;
public doTask(): void {
this.doJob()
.success((data) => this.processData(data));
}
private processData(data: any): void {
this.abc = data.id;
}
}
You can modify your code example to take better advantage of TypeScript and avoid the need to define a self variable. Consider the following.
class X {
abc = 1;
static $inject = ['$http'];
constructor(private $http: ng.IHttpService) { }
doTask() {
this.$http({
xx
})
.success(data => {
this.abc = <number>data;
});
}
}
The body of doTask is defined in a function rather than a lambda. When TypeScript sees a function (named or anonymous), it uses it as the scope that inner this references should apply to. TypeScript implements this by creating a _this variable in the .js file at the function level, which mirrors the self variable you were looking to create by hand.
Just as it is important to use a function at the outer scope, it is important to use a lambda for the success handler. If you had coded the handler like the following, it would create a new "this scope", causing _this to not be generated.
.success(function (data) {
this.abc = <number>data; // Not desired behavior - creates a new abc variable
});

How can I preserve lexical scope in TypeScript with a callback function

I have a TypeScript class, with a function that I intend to use as a callback:
removeRow(_this:MyClass): void {
...
// 'this' is now the window object
// I must use '_this' to get the class itself
...
}
I pass it in to another function
this.deleteRow(this.removeRow);
which in turn calls a jQuery Ajax method, which if successful, invokes the callback like this:
deleteItem(removeRowCallback: (_this:MyClass) => void ): void {
$.ajax(action, {
data: { "id": id },
type: "POST"
})
.done(() => {
removeRowCallback(this);
})
.fail(() => {
alert("There was an error!");
});
}
The only way I can preserve the 'this' reference to my class is to pass it on to the callback, as demonstrated above. It works, but it's pants code. If I don't wire up the 'this' like this (sorry), then any reference to this in the callback method has reverted to the Window object. Because I'm using arrow functions all the way, I expected that the 'this' would be the class itself, as it is elsewhere in my class.
Anyone know how to pass callbacks around in TypeScript, preserving lexical scope?
Edit 2014-01-28:
New readers, make sure you check out Zac's answer below.
He has a much neater solution that will let you define and instantiate a scoped function in the class definition using the fat arrow syntax.
The only thing I will add is that, in regard to option 5 in Zac's answer, it's possible to specify the method signature and return type without any repetition using this syntax:
public myMethod = (prop1: number): string => {
return 'asdf';
}
Edit 2013-05-28:
The syntax for defining a function property type has changed (since TypeScript version 0.8).
Previously you would define a function type like this:
class Test {
removeRow: (): void;
}
This has now changed to:
class Test {
removeRow: () => void;
}
I have updated my answer below to include this new change.
As a further aside: If you need to define multiple function signatures for the same function name (e.g. runtime function overloading) then you can use the object map notation (this is used extensively in the jQuery descriptor file):
class Test {
removeRow: {
(): void;
(param: string): string;
};
}
You need to define the signature for removeRow() as a property on your class but assign the implementation in the constructor.
There are a few different ways you can do this.
Option 1
class Test {
// Define the method signature here.
removeRow: () => void;
constructor (){
// Implement the method using the fat arrow syntax.
this.removeRow = () => {
// Perform your logic to remove the row.
// Reference `this` as needed.
}
}
}
If you want to keep your constructor minimal then you can just keep the removeRow method in the class definition and just assign a proxy function in the constructor:
Option 2
class Test {
// Again, define the method signature here.
removeRowProxy: () => void;
constructor (){
// Assign the method implementation here.
this.removeRowProxy = () => {
this.removeRow.apply(this, arguments);
}
}
removeRow(): void {
// ... removeRow logic here.
}
}
Option 3
And finally, if you're using a library like underscore or jQuery then you can just use their utility method to create the proxy:
class Test {
// Define the method signature here.
removeRowProxy: () => void;
constructor (){
// Use jQuery to bind removeRow to this instance.
this.removeRowProxy = $.proxy(this.removeRow, this);
}
removeRow(): void {
// ... removeRow logic here.
}
}
Then you can tidy up your deleteItem method a bit:
// Specify `Function` as the callback type.
// NOTE: You can define a specific signature if needed.
deleteItem(removeRowCallback: Function ): void {
$.ajax(action, {
data: { "id": id },
type: "POST"
})
// Pass the callback here.
//
// You don't need the fat arrow syntax here
// because the callback has already been bound
// to the correct scope.
.done(removeRowCallback)
.fail(() => {
alert("There was an error!");
});
}
UPDATE: See Sly's updated answer. It incorporates an improved version of the options below.
ANOTHER UPDATE: Generics
Sometimes you want to specify a generic type in a function signature without having to specify it on the the whole class. It took me a few tries to figure out the syntax, so I thought it might be worth sharing:
class MyClass { //no type parameter necessary here
public myGenericMethod = <T>(someArg:string): QPromise<T> => {
//implementation here...
}
}
Option 4
Here are a couple more syntaxes to add to Sly_cardinal's answer. These examples keep the function declaration and implementation in the same place:
class Test {
// Define the method signature AND IMPLEMENTATION here.
public removeRow: () => void = () => {
// Perform your logic to remove the row.
// Reference `this` as needed.
}
constructor (){
}
}
or
Option 5
A little more compact, but gives up explicit return type (the compiler should infer the return type anyway if not explicit):
class Test {
// Define implementation with implicit signature and correct lexical scope.
public removeRow = () => {
// Perform your logic to remove the row.
// Reference `this` as needed.
}
constructor (){
}
}
Use .bind() to preserve context within the callback.
Working code example:
window.addEventListener(
"resize",
(()=>{this.retrieveDimensionsFromElement();}).bind(this)
)
The code in original question would become something like this:
$.ajax(action, {
data: { "id": id },
type: "POST"
})
.done(
(() => {
removeRowCallback();
}).bind(this)
)
It will set the context (this) inside the callback function to whatever was passed as an argument to bind function, in this case the original this object.
This is sort of a cross post from another answer (Is there an alias for 'this' in TypeScript?). I re-applied the concept using the examples from above. I like it better than the options above because it explictly supports "this" scoping to both the class instance as well as the dynamic context entity that calls the method.
There are two versions below. I like the first one because the compiler assists in using it correctly (you won't as easily try to misuse the callback lambda itself as the callback, because of the explicitly typed parameter).
Test it out:
http://www.typescriptlang.org/Playground/
class Test {
private testString: string = "Fancy this!";
// Define the method signature here.
removeRowLambdaCallback(outerThis: Test): {(): void} {
alert("Defining callback for consumption");
return function(){
alert(outerThis.testString); // lexically scoped class instance
alert(this); // dynamically scoped context caller
// Put logic here for removing rows. Can refer to class
// instance as well as "this" passed by a library such as JQuery or D3.
}
}
// This approach looks nicer, but is more dangerous
// because someone might use this method itself, rather
// than the return value, as a callback.
anotherRemoveRowLambdaCallback(): {(): void} {
var outerThis = this;
alert("Defining another callback for consumption");
return function(){
alert(outerThis.testString); // lexically scoped class instance
alert(this); // dynamically scoped context caller
// Put logic here for removing rows. Can refer to class
// instance as well as "this" passed by a library such as JQuery or D3.
}
}
}
var t = new Test();
var callback1 = t.removeRowLambdaCallback(t);
var callback2 = t.anotherRemoveRowLambdaCallback();
callback1();
callback2();
Building upon sly and Zac's answers with types:
A complete hello world example. I hope this is welcome, seeing as this is the top result in Google, when searching for "typescript javascript callbacks"
type MyCallback = () => string;
class HelloWorld {
// The callback
public callback: MyCallback = () => {
return 'world';
}
// The caller
public caller(callback: MyCallback) {
alert('Hello ' + callback());
}
}
let hello = new HelloWorld();
hello.caller(hello.callback);
This gets transpiled into:
var HelloWorld = (function () {
function HelloWorld() {
// The callback
this.callback = function () {
return 'world';
};
}
// The caller
HelloWorld.prototype.caller = function (callback) {
alert('Hello ' + callback());
};
return HelloWorld;
}());
var hello = new HelloWorld();
hello.caller(hello.callback);
Hope someone finds it just a little useful. :)

Categories