SignalR class method callback and 'this' [duplicate] - javascript

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 5 years ago.
I had a similar question but not the same. This one focuses on the new ES6 class keyword and how to handle this. SignalR is calling a class method. When inside that class method 'this' refers to the SignalR hub not the class instance itself. How can I get the class instance inside this class member that SignalR called using the new ES6 classes?
class gameLoad {
constructor(){
}
init(){
// create the network object and hook it's functions update for loading
this.network = $.connection.testHub;
this.network.client.hello = this.sayHello;
}
// this is called from signalR and 'this' now refers to the signalR hub inside this function. how can I get the class instance?
sayHello(){
console.log(this); // 'this' refers to signalR object not gameLoad object
}
create(){
var self = this;
$.connection.hub.start().done(function () {
console.log("Started!")
console.log("Calling server function.");
// make our first network call which will turn around and call client.hello which is bound to this classes sayHello() member function
self.network.server.hello();
});
}
}

When using classes, it's best to use arrow functions so that you have 'this' properly set.
In your example, you assign sayHello to client's hello method. You will need to bind gameLoad to it at that time:
this.network.client.hello = this.sayHello.bind(gameLoad);
Alternatively, you can convert sayHello into an arrow function:
sayHello = () => {

Related

JavaScript Event Listeners inside methods [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 3 years ago.
I was hoping to be able to call object methods from an event listener, however, I am unable to access object properties on the called function:
import {action} from '../desktopIntegration/actions.js'
class Object {
constructor() {
this.property = 2
}
addListener() {
action.on("keyup", this.printEvent)
}
printEvent() {
console.log(this.property)
}
}
This code gives the error:
unable to access property of undefined
when addListener is called.
Is there a way to make this work? I want to keep the callback as a method function so that I can delete the listener on each instance of Object.
Thanks!
Instance methods are not bound to the class instance by default. If a method is being used as an event listener, it is by default bound to the element emitting the event.
To fix that, you need to overwrite your method in the constructor, by a version bound to the class instance:
class Foo {
constructor() {
this.property = 2
this.printEvent = this.printEvent.bind(this)
this.addListener()
}
addListener() {
document.getElementById('action').addEventListener("keyup", this.printEvent)
}
printEvent(event) { // added the event argument, gets automatically passed to the listener
console.log(this.property)
console.log(event.target.value) // to access the event emitting element, use event.target
}
}
const bar = new Foo();
<input id="action" />
Also note that you should NEVER call your class Object.

How to call a class method from inside same class method? [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 3 years ago.
I've created a class and I'm trying to call a method from inside that class from another method inside that same class. I've seen online that some people say use 'this' keyword, but as you can see from the example, that doesn't work. Can someone please explain in simple terms how to call a class method from inside another method in that same class? Thanks!
class Form {
constructor() {
}
fn1 () {
console.log('fn1 calling fn2 now:')
this.fn2();
}
fn2 () {
console.log('fn1 called me')
}
}
let myForm = new Form();
myForm.fn1();
Example can be found here
The error I keep getting is this: Uncaught TypeError: this.fn2 is not a function
There's nothing wrong with the code you show embedded into your question - that should work just fine. The issue arises when you pass a method to .addEventListener() as a callback (which shows in the code you have a link to).
Change this:
document.getElementById('call-func').addEventListener('click', this.fn1);
to this:
document.getElementById('call-func').addEventListener('click', this.fn1.bind(this));
The problem is that passing this.fn1 loses the value of this so when the listener calls the callback later, the object that it belongs to is lost and then this inside of fn1() is wrong. In addition, .addEventListener() explicitly sets the this value to the DOM element that triggered the event (which is not your object).
The same thing would happen if you did this:
let f = this.fn1;
f();
Using .bind(this) rebinds the value of this when the listener calls it.
FYI, you could also do it this way with a fat-arrow function:
document.getElementById('call-func').addEventListener('click', () => this.fn1());
Or, an older way of doing it:
var self = this;
document.getElementById('call-func').addEventListener('click', function() {
self.fn1();
});
My preference is to use .bind(this).
More explanation on event listeners and this here: "This" within es6 class method
More explanation on the general concept of how this gets set when a function is called here: When you pass 'this' as an argument

Uncaught TypeError: this.method is not a function - Node js class export [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I am new to node.js and I am trying to require a class. I have used https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes as reference. However, when I do this for example:
// talker.js
class Talker {
talk(msg) {
console.log(this.say(msg))
var t = setTimeout(this.talk, 5000, 'hello again');
}
say(msg) {
return msg
}
}
export default Talker
// app.js
import Talker from './taker.js'
const talker = new Talker()
talker.talk('hello')
I get:
talker.js:4 Uncaught TypeError: this.say is not a function
It should be said that app.js is the electron.js renderer process and it bundled using rollup.js
Any ideas why this would be?
Update: Sorry, I forgot to add in a line when putting in the psuedo code. It actually happens when I call setTimeout with callback. I have updated the code.
You are losing the bind of this to your method.
Change from this:
setTimeout(this.talk, 5000, 'hello again');
to this:
setTimeout(this.talk.bind(this), 5000, 'hello again');
When you pass this.talk as a function argument, it takes this and looks up the method talk and passes a reference to that function. But, it only passes a reference to that function. There is no longer any association with the object you had in this. .bind() allows you to pass a reference to a tiny stub function that will keep track of this and call your method as this.say(), not just as say().
You can see the same thing if you just did this:
const talker = new Talker();'
const fn = talker.say;
fn();
This would generate the same issue because assigning the method to fn takes no associate to talker with it at all. It's just a function reference without any association with an object. In fact:
talker.say === Talker.prototype.say
What .bind() does is create a small stub function that will save the object value and will then call your method using that object.

Cannot call a method within a class it defined it in ES6 in Node.js [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I am making an app using Node.js, Express.js and MongoDB.
I am using a MVC pattern and also have separate file for routes.
I am trying me make a Controller class, in which a method calls another method declared within it. But I cannot seem to be able to do this. I get "Cannot read property '' of undefined".
index.js file
let express = require('express');
let app = express();
let productController = require('../controllers/ProductController');
app.post('/product', productController.create);
http.createServer(app).listen('3000');
ProductController.js file
class ProductController {
constructor(){}
create(){
console.log('Checking if the following logs:');
this.callme();
}
callme(){
console.log('yes');
}
}
module.exports = new ProductController();
When I run this I get following error message:
Cannot read property 'callme' of undefined
I have ran this code by itself with little modification as following and it works.
class ProductController {
constructor(){}
create(){
console.log('Checking if the following logs:');
this.callme();
}
callme(){
console.log('yes');
}
}
let product = new ProductController();
product.create();
Why does one work and not the other?
HELP!
Your method is being rebound to the Layer class within express, losing its original context. The way that express handles routes is by wrapping each one in a Layer class, which assigns the route callback to itself:
this.handle = fn;
That is where your problems arise, this assignment automatically rebinds the function context to Layer. Here is a simple example demonstrating the problem:
function Example() {
this.message = "I have my own scope";
}
Example.prototype.logThis = function() {
console.log(this);
}
function ReassignedScope(logThisFn) {
this.message = "This is my scope now";
// simulation of what is happening within Express's Layer
this.logThis = logThisFn;
}
let example = new Example()
let scopeProblem = new ReassignedScope(example.logThis);
scopeProblem.logThis(); // This is my scope now
Others have already pointed out the solution, which is to explicitly bind your method to the ProductController instance:
app.post('/product', productController.create.bind(productController));
When you pass create method as method it is probably called in different context (this) as you expect. You can bind it:
app.post('/product', productController.create.bind(productController));
There are many other ways how to ensure this refers to correct object.
E.g. wrap it with function (either arrow or classical):
app.post('/product', (...args) => productController.create(...args));
Or bind methods in constructor:
constructor() {
this.create = this.create.bind(this);
}

ES6 Class: access to 'this' with 'addEventListener' applied on method [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 7 years ago.
In this es6 script, the click event don't works because sayHello method is called with this.elm (<div>) as this.
how to associate a event to a method without loose the scope?
class player{
constructor (name) {
this.name = name;
this.elm = document.createElement('div');
this.elm.addEventListener('click', this.sayHello);
}
sayHello() {
console.log(this.name + ' say: "hello!"'); // 'undefined say 'hello!"';
}
kill() {
console.log(`RIP ${this.name} :'(`);
this.elm.addClass('dead');
this.elm.removeEventListener('click', this.sayHello);
}
}
This is a general JS issue, but the core of it is that
this.elm.addEventListener('click', this.sayHello);
is no different than
var fn = this.sayHello;
this.elm.addEventListener('click', fn);
You are passing a function as the event handler, but have not ensured that when fn is called that this will be set to your desired value. The easiest way to do this in ES5 would be
this.elm.addEventListener('click', this.sayHello.bind(this));
or in ES6, using an arrow function:
this.elm.addEventListener('click', evt => this.sayHello(evt));
Note however that both of these solutions will break your (already slightly broken) logic in kill because
this.elm.removeEventListener('click', /* what? */);
You don't have any reference to the function that you attached anymore, so you have no way of removing the event handler.
I'd suggest two options:
// Create a new function that is bound, and give it a new name
// so that the 'this.sayHello()' call still works.
this.boundSayHello = evt => this.sayHello(evt);
this.elm.addEventListener('click', this.boundSayHello);
this.elm.removeEventListener('click', this.boundSayHello);
or
// Bind the function with the same name and use `.bind` instead of the
// arrow function option.
this.sayHello = this.sayHello.bind(this);
this.elm.addEventListener('click', this.sayHello);
this.elm.removeEventListener('click', this.sayHello);

Categories

Resources