Im trying to build an app with Cytoscape.js and Angular2+.
The Core part take place in a single service (cy.service).
I add all Eventlisteners in a single function and call it after the cy init.
eg:
initEventlisteners(){
cy.on('tap', this.handler);
cy.on(...);
...
}
handler(event: any){
console.log('something');
}
If I wrap console.log in a helper-function like that:
helper(){
console.log('something');
)
And use it in the handler-function
handler(event:any){
this.helper();
}
It calls: helper() is not a function.
Thats a big problem that a cant use other functions inside the handler.
Any ideas how to solve that problem ?
That's because with binding like this cy.on('tap', this.handler); you are loosing scope of variable. You should use it in this way cy.on('tap', (event) => { this.handler(event) }); (if it's passing any event object) or cy.on('tap', this.handler.bind(this));.
The frist approach with () => { } is called arrow function, which preserve current scope of variables. It's a new feature of ES6 specification. However you don't have to be afraid, angular compiler will convert it to backward compatible to ES5.
The second approach .bind(this) just simply binding this method to current scope where it's was called.
Related
Using TypeScript (JavaScript and Angular):
I want lodash's throttle decorator to limit an API call while the user is navigating around the page, but still fire before they unload (leave) the site.
In the typescript constructor I have window.addEventListener('beforeunload', () => this.onUnload());
with the onUnload() function being declared as
onUnload() {
this.thisIsTheThrottledFunction.flush;
}
but I am getting the error response "Property 'flush' does not exist on type '() => Promise'."
The function whose .flush method I am trying to access is the declared throttled version of the other function. The function is successfully throttled, so I am confident that part of the code works. What is the best way to access the .flush method?
You should be able to debug this by verifying what the value of this is. Seems to me like you just need to bind the object's this value to the onUnload function (or you can pass it in). For instance, you could put this in your constructor: this.onUnload = this.onUnload.bind(this). There's a sugar for this syntax, where you define it in your class using onUnload = () => { ... }. Both of those methods attach the method to the instance instead of just having it as part of the prototype. Or, you could pass the bound function directly to your event listener:
window.addEventListener('beforeunload', this.onUnload.bind(this));
I'm using angular w/ rxjs to observe user events on the interface. However, I'm having this really simple problem with passing arguments to a method in an arrow function. Here is the problem:
This is not working: searchterm is not being passed to this.searchFacilities
ngOnInit() {
this.searchTerm$.subscribe(searchterm => this.searchFacilities);/**Not working here**/
}
searchFacilities(term: string){
console.log(term);
this.facilityservice.searchFacilities(term)
.subscribe(results => this.facilityList = results);
}
But this works:
this.searchTerm$.subscribe(searchterm => { this.searchFacilities(searchterm); })
Clearly, I have other solutions that are pretty painless, but I really want to understand why my first approach is not working. Thanks!
Because the parameter is not passed directly to your function.
Example from the doc:
Rx.Observable.range(0, 3).subscribe(function (x) { console.log(x) });
The same example with an arrow function:
Rx.Observable.range(0, 3).subscribe(x => console.log(x));
Small clarification. The doc says you need to pass a callback to subscribe() and that this callback will receive the value(s) emitted by the observable.
We could write it like this:
const myCallBack = (val) => console.log(val);
Observable.range(0, 3).subscribe(myCallBack);
In your case you already have a callback, this.searchFacilities.
This means you can simply write:
this.searchTerm$.subscribe(this.searchFacilities);
Just like you can rewrite my original example to:
// Now `console.log` is the callback!
Observable.range(0, 3).subscribe(console.log);
In other words, the problem is not "Why arrow function is not passing arguments". The problem is that you created an arrow function whose body IS your callback, instead of just using your callback directly.
The expanded version of your code would look like this:
const myCallBack = (searchterm) => {
return this.searchFacilities;
}
As you can see, searchFacilities is neither invoked nor does it receive the searchterm param.
You could have run into the same problem with a NON-ARROW function by writing the following code (although the syntax of arrow functions does make the mistake more likely and insidious):
const myCallBack = function(searchterm) {
return this.searchFacilities;
}
Because you're getting a reference to the searchTerm but you're not doing anything with it. You could just do this.searchTerm$.subscribe(this.searchFacilities) and the term will be passed into the function.
You searchFacilities function is declared in global scope, and then is called inside of ngOnInit as a callback and its context is not anymore global scope, now this points to ngOnInit element Object. In order to work inside ngOnInit object you need to bind it and then searchFacilities be method of ngOnInit and in this way its going to work.
I find that it's very common to have code like (this is TypeScript, but the equivalent JS is fairly obvious and this is really a JS problem, although one that TS could solve):
class Foo {
someField;
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Fails because `this` is not the instance of `Foo`
});
}
}
The solution, of course, is to use Function.bind() like so:
someMethod() {
doSomethingTakingACallback(function() {
this.someField; // Works as expected
}.bind(this));
}
Now the problem is that I have callbacks that must access the object instance a lot (ie, I need to be calling bind(this) on many callbacks). I've now wasted more time on bugs resulting from this than I'd like to admit. Is there some alternative? Am I missing some easier way to do this? Is there any reason that this is the default behavior as opposed to the function that we get from calling bind(this)?
One solution I know of is to do something like var me = this and then call me.someField (etc). It's a little nicer looking when I have many callbacks or nested callbacks. Although then I lose out on the iconicness of this, which I feel makes it the most clear where the member is located (on the class that I'm writing).
Typescript and ES6/ES2015 both support the "fat arrow function" syntax, which allows you to use this the way you do in most other languages - to refer to the class instance object.
e.g.
someMethod() {
doSomethingTakingACallback(() => {
this.someField;
});
}
This compiles to the following code:
Foo.prototype.someMethod = function () {
var _this = this;
doSomethingTakingACallback(function () {
_this.someField;
});
};
ES6 Arrow functions keep the this of the surrounding scope and thus do not need binding.
ES7 will (possibly) have the :: bind operator
Assign this to another variable outside of your callback method and use that.
class Foo {
someField : any;
someMethod() {
var that = this;
doSomethingTakingACallback(function () {
// use that.someField;
});
}
}
I need to access a React component within a function nested within a a handler function.
When in a nested function this does not give a reference to the component
so I explicitly declare it instead of doing the usual this.setState(...)
Is there a cleaner way to do this than my following code?
React.createClass({
uploadImage : function() {
...
var componentReference = this
xhrRequest.onreadystatechange = function()
{if(request.readyState==4) {
componentReference.setState({uploadStatus: request.status});
}
}
This is just how JavaScript works:
You'll have to keep a reference to whatever you want to access in a function that's called in a different context (like you implemented it with your variable)…
or
…bind what this points to at the time the function is called. There's no need to use a library (like lodash) because ECMAScript 5.1 has his built-in, and you can use it in any recent-ish browser (Chrome 7+, Firefox 4+, IE 9+, Safari 5.1+), see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
If you need to support older browsers, you can implement a bind function yourself, see for example Underscore.js' Function.prototype.bind implementation: http://underscorejs.org/#bind
The most idiomatic way to do this is with the native .bind() method, no libraries required:
React.createClass({
uploadImage : function() {
// ...
xhrRequest.onreadystatechange = function() {
if(request.readyState == 4) {
this.setState({ uploadStatus: request.status });
}
}.bind(this)
// ...
}
})
.bind(this) sets the context of this inside the .onreadystatechange closure. It's a bit like passing the scope to the inner function.
If you are using e.g. lodash, you can do it like this:
xhrRequest.onreadystatechange = _.bind(function () {
if ( request.readyState == 4 ) {
this.setState({ uploadStatus: request.status });
}
}, this);
Or, if you use jQuery, you can proxy.
You can simply use the Function bind method in plain old JS like function() {}.bind(this) and then "this" inside your function will be your React component.
You can see the definition of bind() here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
You could put the onreadystatechange handler on the “class” itself, profiting from React’s auto binding.
You could also keep a reference to setState, but I’m not sure whether that’s auto-bound as well. Try it out.
Last but not least, since you are probably using a transpiler anyway, you could use an ES6 arrow function. Arrow functions use the “outer” value of this (lexical this)
I have a makeshift events system in JS and the attacher looks like this:
events.attach(events.NEW_TASK,this.update);
Where this.update is a callback/handler/function. The problem is, when the dispatcher tries to call this handler, it loses it's original context. How can I bind the handler with the context of the object that calls the attach function without passing this as an argument and using that?
Edit: I should also mention that it will be somewhat of a framework so it has to be simple for other developers so preferably not needing to store the context in another variable.
Another option, using the Function.prototype.bind method, part of the new ECMAScript Standard:
events.attach(events.NEW_TASK, this.update.bind(this));
Until ECMAScript 5 has wide adoption in browsers, you could use your own implementation of Function.prototype.bind:
function bind(func, thisObj) {
return function() {
func.apply(thisObj, arguments);
};
}
events.attach(events.NEW_TASK, bind(this.update, this));
You could store the reference of "this" in a local variable and try calling the function using it
var that = this;
events.attach( events.NEW_TASK, function( ) { that.update(); } );