`this` is undefined in expressJS route handler
2020 Update
- While both solutions posted by @robertkiep get the job dont, I want to emphasize that both look ugly and are not maintainable
- Method 1 which does router.get('/groups/', groupsCtrl.get.bind(groupsCtrl)) looks really ugly when you have a large number of routes
- Method 2 gets cumbersome when your controller has many routes
- Since your example has only 1 route let me illustrate the problem
Using method 2
class AuthController {
constructor({ db, pgp, logger }) {
super({ db, pgp, logger })
this.postLogin = this.postLogin.bind(this)
this.postLogout = this.postLogout.bind(this)
this.postSignup = this.postSignup.bind(this)
this.postForgot = this.postForgot.bind(this)
this.getReset = this.getReset.bind(this)
this.postReset = this.postReset.bind(this)
}
postLogin(req, res, next) {
}
postLogout(req, res, next) {
}
async postSignup(req, res, next) {
}
async postForgot(req, res, next) {
}
async getReset(req, res, next) {
}
async postReset(req, res, next) {
}
}
Each time you add a new method, the constructor needs to be updated further
Method 3
This in my opinion is a lot cleaner, doesnt need maintenance and you can keep adding methods as you want
- The idea is to use the Object.hasOwnPropertyName to get an array of all method names and then bind them programmatically
- For example if you write Object.hasOwnPropertyName(AuthController.prototype) it will give you ALL NON STATIC methods in an array
- In the example above you will get ['constructor', 'postLogin', 'postLogout'...]
- If you call Object.hasOwnPropertyName(AuthController) you get STATIC methods
- Lets invoke them programmatically
This controller requires little to no maintenance except keep the static and non static methods in mind, remove the constructor by filtering it out and then invoke bind on each
class AuthController {
constructor({ db, pgp, logger }) {
super({ db, pgp, logger })
this.postLogin = this.postLogin.bind(this)
this.postLogout = this.postLogout.bind(this)
this.postSignup = this.postSignup.bind(this)
this.postForgot = this.postForgot.bind(this)
this.getReset = this.getReset.bind(this)
this.postReset = this.postReset.bind(this)
Object.getOwnPropertyNames(AuthController.prototype)
.filter((propertyName) => propertyName !== 'constructor')
.forEach((method) => (this[method] = this[method].bind(this)))
}
postLogin(req, res, next) {
}
postLogout(req, res, next) {
}
async postSignup(req, res, next) {
}
async postForgot(req, res, next) {
}
async getReset(req, res, next) {
}
async postReset(req, res, next) {
}
}
The answer above is great, I want to add a little bit to help clarify:
Assume we have a class:
class TClass {
constructor(arg) {
this.arg = arg
}
test() {
console.log(this.arg)
}
}
This will NOT work:
const t = new TClass("test")
const method = t.test // method is simply a reference without context
method() // 'this' is not defined
This will work:
const t = new TClass("test")
t.test() // log 'test'
And the reason is like the comments above, the reference to the function doesn't have a context
You need to bind the method to the instance.
One solution:
router.get('/groups/', groupsCtrl.get.bind(groupsCtrl));
Another solution:
constructor() {
this.info = "test";
this.get = this.get.bind(this);
}
Or use something like es6bindall
(which basically does the same as the code above, but is perhaps a bit more useful when you need to bind more than one method).
class groupsCtrl {
constructor() {
this.info = 'test';
}
get = (res, req) => {
console.log('LOG ! ', JSON.stringify(this));
};
}
You can just use arrow function to avoid boilerplate code