J'ai essayé de rechercher ce qui semble être tout Internet, mais je suis toujours contrarié par un problème avec une classe JS que j'écris pour un Micro Service (toujours en apprentissage un peu).
Donc, j'essaie d'appeler une méthode de classe sur un objet instancié, et selon mes connaissances et mes tests unitaires (défectueux je suppose) cela devrait fonctionner.
Eh bien, je vais commencer par l'erreur que je reçois:
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)
(Et beaucoup plus).
Le code appelant le 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
Appelant du contrôleur
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;
};
Je ne sais vraiment pas pourquoi this
non défini dans UserController
.
Toute aide serait grandement appréciée.
Lorsque vous procédez ainsi:
router.get('/users', userController.list);
ce qui est transmis à votre routeur n'est qu'une référence à la méthode .list
. L'instance userController est perdue. Ce n'est pas unique aux routeurs - c'est une propriété générique de la façon dont les choses sont passées en Javascript. Pour mieux comprendre, ce que vous faites essentiellement est le suivant:
let list = userController.list;
// at this point the list variable has no connection at all to userController
router.get('/users', list);
Et, en mode strict
de Javascript, lorsque vous appelez une fonction régulière sans référence d'objet telle que l'appel de list()
ci-dessus, alors this
sera undefined
à l'intérieur la fonction. C'est ce qui se passe dans votre exemple. Pour y remédier, vous devez vous assurer que votre méthode est appelée avec une référence d'objet appropriée comme dans userController.list(...)
afin que l'interpréteur définisse la valeur this
de manière appropriée.
Il existe plusieurs façons de résoudre ce problème:
Créez votre propre wrapper de fonction
router.get('/users', function(req, res) {
userController.list(req, res);
});
Cela fonctionne dans n'importe quelle version de Javascript.
en utilisant .bind()
pour créer un wrapper pour vous qui l'appelle avec le bon objet
router.get('/users', userController.list.bind(userController));
Cela fonctionne dans ES5 + ou avec un polyfill .bind()
.
tilisez un raccourci de la fonction de flèche ES6
router.get('/users', (...args) => userController.list(...args));
Cela fonctionne dans ES6 +
Personnellement, je préfère l'implémentation de .bind()
parce que je pense qu'elle est juste plus simple et plus déclarative/plus claire que les autres et le "raccourci" ES6 n'est pas vraiment plus court.
router.get()
n'appellera pas votre classe comme vous le pensez. Vous lui donnez une référence à une fonction qu'il appellera dans le cadre du router.get
ce qui signifie que ce ne sera pas dans le contexte de votre userController.
Vous pouvez résoudre ce problème en faisant:
router.get('/users', function(){userController.list(...arguments)});
En d'autres termes, ne pas utiliser express userController
pour list
, utiliser express une fermeture qui aura userController
appeler list
avec les arguments donnés .