Why is "this" undefined in this class method? - javascript

I've tried to search over what seems to be the entire internet, but I'm still vexed by a problem with a JS class I'm writing for a Micro Service (still in learning a bit).
So, I try to call a class method on an instantiated object, and according to my knowledge and my (faulty I presume) unit tests it should work.
Well, I'll start with the error I receive:
GET /api/users 500 2.863 ms - 2649
TypeError: Cannot read property 'repository' of undefined
at list (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\controllers\user-controller.js:20:9)
at Layer.handle [as handle_request] (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\node_modules\express\lib\router\layer.js:95:5)
at next (C:\Users\<user>\Documents\Programming\node\kaguwa-ngn\kaguwa-user-service\node_modules\express\lib\router\route.js:137:13)
(And a lot more).
The code calling code:
user-controller.js
'use strict';
var utils = require('./utils');
class UserController {
constructor(repository) {
this.repository = repository || {};
}
/**
*
* Lists all users.
*
* #param {object} req
* #param {object} res
*/
list(req, res) {
this.repository.list(function (err, users) {
if (err) return res.status(500).json(utils.createError(500));
if (Object.keys(users).length !== 0) {
res.json(users);
} else {
res.status(404).json(utils.createNotFound('user', true));
}
});
}
// more code
}
module.exports = UserController
Caller of controller
user-api.js
'use strict';
var express = require('express');
var UserController = require('../controllers/user-controller');
var router = express.Router();
module.exports = function (options) {
var userController = new UserController(options.repository);
router.get('/users', userController.list);
// Mode code
return router;
};
I really jave no idea on why this undefined in UserController.
Any help would be greatly appriciated.

When you do this:
router.get('/users', userController.list);
what gets passed to your router is just a reference to the .list method. The userController instance gets lost. This is not unique to routers - this is a generic property of how things are passed in Javascript. To understand further, what you are essentially doing is this:
let list = userController.list;
// at this point the list variable has no connection at all to userController
router.get('/users', list);
And, in Javascript's strict mode, when you call a regular function without any object reference such as calling list() above, then this will be undefined inside the function. That is what is happening in your example. To fix it, you need to make sure that your method is called with a proper object reference as in userController.list(...) so that the interpreter sets the this value appropriately.
There are multiple ways to solve this problem:
Make your own function wrapper
router.get('/users', function(req, res) {
userController.list(req, res);
});
This works in any version of Javascript.
Using .bind() to make a wrapper for you that calls it with the right object
router.get('/users', userController.list.bind(userController));
This works in ES5+ or with a .bind() polyfill.
Use an ES6 arrow function shortcut
router.get('/users', (...args) => userController.list(...args));
This works in ES6+
Personally, I prefer the .bind() implementation because I think it's just simpler and more declarative/clearer than any of the others and the ES6 "shortcut" isn't really shorter.

router.get() won't call your class the way you think it will. You are giving it a reference to a function which it will call in the context of the router.get meaning, it won't be in the context of your userController.
You can fix this by doing:
router.get('/users', function(){userController.list(...arguments)});
In other words, don't have express use userController's reference to list, have express use a closure that will have userController call list with the given arguments.

Related

Unable to call class methods inside its constructor

I'm struggling with the implementation of my express router (actually this is a "subrouter" of my main router, that's why I have to extends express.Router)
I have the following code (as an example I simplified it to have only one method) :
import express from "express";
export default class MandatoryFieldsSettingsRouter extends express.Router {
constructor() {
super();
this.get('/', this.retrieveMandatoryFieldsSettings);
}
async retrieveMandatoryFieldsSettings(req, res) {
//some treatment here
}
}
So in the file creating the main router of the application I can defined my subpath like so :
router.use('/mandatory-fields-settings', new MandatoryFieldsSettingsRouter());
I have the following error Error: Route.get() requires a callback function but got a [object Undefined] at application startup because this.retrieveMandatoryFieldsSettings is undefined inside the constructor.
I fixed it using this different method declaration :
// 1st impl : this one is the "wrong" one causing my error
async retrieveMandatoryFieldsSettings(req, res) {
//some treatment here
}
// 2nd impl : this one is the "good" one making my application working
retrieveMandatoryFieldsSettings = async function (req, res) {
//some treatment here
}
For some reason my company wants me to use the first implementation. Is there any way to use it and keep the way I declare my path inside my constructor ? (I think it's more readable to see every paths declared in the current class just by looking at its constructor).
My company isn't closeminded, if there is absolutely no reason to "ban" the second implementation just let me know and please explain what's the difference between the two if you know it (-> why the first got undefined and the second not ?)
Thanks !
You can use the first approach if you use the ES2020 syntax for private members (#):
async #retrieveMandatoryFieldsSettings(req, res) {
//some treatment here
}
Regards,
Craig

Can't access object property in javascript variable

I've struggled for a while seek for help. This is really strange. I want to access an object property, but this always throws an error:
TypeError : cannot read property template of undefined
But my application works properly. Just there is notification output if can't access template of undefined
//this is my object variabel
var login = {};
login.data = {
checkInput : formValidation,
userSchema : User,
template : 'pages/users/login',
}
// so I add new method which I call in different files
login.header = async(req, res, next) => {
/// in this section function I want to read property of template but it always return undefined
try {
// I have some code with read databases here
//some data i want to render
var data = {};
res.render(this.data.template,data);
// I've been also trying another way.
var template = login.data.template !== undefined ? 'page/users/login' : login.data.template;
res.render(login.data.template, data);
// both of above always return output, but can't read template of undefined
} catch(e) {
throw new Error(e);
}
}
It's because you're using an arrow function which loses the binding to login (the this you're accessing is an attempt to access the login object). Use a regular ES5 function.
login.header = async function(req, res, next) {...};
As found in the docs:
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
i've been trying to access with property with no array function
var login = {}
login.data = {
template : 'admin/pages/'
body : {}
};
login.getViews = function(req, res, next) {
// it'l throw an error
res.render(this.data.template, this.data.body);
// than i try with another way, it works
res.render(login.data.template, login.data.body);
}
so why i could't access with property this even i use no rows function??

new object in constructor from class undefined

I'm creating a new object from a class in a constructor, and whenever it runs I get an error that operate is undefined in the method, though it is defined in the constructor. Operate itself is thoroughly tested and works great in a separate context so that's not the problem. I'm building it with Babel, not running it directly in Node 7.0.0
import Operate from "./operate"
export default class {
constructor(Schema) {
this.schema = Schema
this.operate = new Operate(this.schema)
console.log(this.operate.run) // <- Logs just fine
}
update(req, res) {
console.log(this.operate.run) // <- Nada
this.operate.run(req.body)
.then(value => {
res.status(200).json(value)
})
}
This feels like I'm missing something fundamental. I've heard this isn't a great pattern anyway, so please feel free to suggest a better way. Thanks so much in advance.
UPDATE: This is how update is being used. I don't suspect there's any problem here, as it has worked just fine when I had been importing controller as a function from another module, instead of a class
import {Router, } from "express"
import Controller from "../controller"
import User from "./user.model"
let controller = new Controller(User)
let router = new Router()
router.post("/", controller.update)
module.exports = router
Change from this:
router.post("/", controller.update)
to this:
router.post("/", controller.update.bind(controller))
When you pass controller.update it only passed a pointer to the method and any association with the controller object is lost. Then, when that update method is called later, there is no association with the appropriate object and thus the this handler in the method is wrong and you get the error you were seeing.
You either force the binding of the update method within the object or when you pass the method elsewhere that might not be called correctly, you can use the above structure to pass a bound version of the method.
You could also modify your definition of the update method to permanently bind it to your object in the constructor by adding this to the constructor:
this.update = this.update.bind(this);

Using ES6 classes OR Object Literals in controllers for an Express + NodeJS app

There are 2 things that I'm very confused about.
What is the advantage of using any of ES6 class or Object literals.
And where should I use any of them?
Some of the examples that I'm trying out are mentioned below. Please let me know when to use particular way of implementation and when not to.
Class Example 1:
// auth.js
class Auth {
login(req, res) {...}
signup(req, res) {...}
}
module.exports = new Auth();
// index.js
const auth = require('auth');
Class Example 2:
// auth.js
class Auth {
login(req, res) {...}
signup(req, res) {...}
}
module.exports = Auth;
// index.js
const Auth = require('auth');
const auth = new Auth();
Object Literal Example:
// auth.js
module.exports = {
login: (req, res) => {...},
signup: (req, res) => {...}
};
// index.js
const auth = require('auth');
What I think from reading about them is that:
Class Example 1:
You can not create more than 1 object. Because a module is only executed once. So, on every import you will get the same object. Something similar to singleton. (Correct me here if I misunderstood it)
You will not be able to access the static methods of the class because you are only exporting the object of the class.
Class Example 2:
If you have a class that contains nothing but helper methods and the object does not have any state, It makes no sense creating object of this class all the time. So, in case of helper classes, this should not be used.
Object Literal Example:
You can not do inheritance.
Same object will be passed around on every require. (Correct me if I'm wrong here as well)
Please help me understand these concepts, what I'm missing out, what I've misunderstood and what should be used when and where. I'll be very grateful for your help.
Feel free to edit the question, if you think I made a mistake somewhere.
Class Example 1: You can not create more than 1 object. Because a module is only executed once. So, on every import you will get the same object. Something similar to singleton.
Correct. This is an antipattern therefore. Do not use it. class syntax is no replacement for object literals.
You will not be able to access the static methods of the class because you are only exporting the object of the class.
Theoretically you can do auth.constructor.… but that's no good.
Class Example 2: If you have a class that contains nothing but helper methods and the object does not have any state, It makes no sense creating object of this class all the time. So, in case of helper classes, this should not be used.
Correct. Use a simple object literal instead, or even better: multiple named exports instead of "utility objects".
Object Literal Example: You can not do inheritance.
You still can use Object.create to do inheritance, or parasitic inheritance, or really anything.
Same object will be passed around on every require.
Correct, but that's not a disadvantage. If your object contains state, you should've used a class instead.
If your class has got a constructor, you can build several objects from this class threw :
var Panier= require('./panier');
var panier1 = new Panier(13, 12, 25);
var panier2 = new Panier(1, 32, 569);
Of course your Panier would be defined in the file Panier.js located in the same directory :
module.exports = class Panier
{
constructor(code, qte, prix)
{
this.codeArticle = code;
this.qteArticle = qte;
this.prixArticle = prix;
}
getCode()
{
return this.codeArticle;
}
getQte()
{
return this.qteArticle;
}
getPrix()
{
return this.prixArticle;
}
}

How to launch exports.function in Node.Js

In the routes folder of a Node.Js app I have a file entries.js which has the following function:
exports.form = function(req, res){
res.render('post', { title: 'Post' });
};
Is it actually possible to launch something like this from another exports function in the same file, such as:
exports.something = function(req, res){
this.form(req.res);
};
Where this.form refers to exports.form function in the same file.
Thank you!
The value of this by default will point to the object whose member the function is. In this case, both functions are members of the same object, thus using this.form should work as expected.
The exceptions to this rule are when the function in question is used together with bind, call, or apply.

Categories