i've already seen this
How to access the correct `this` inside a callback
it doesn't mention classes , at least this syntax im using
im new to nodejs and js class so bear with me , im writing a app with mvc like architecture
i got two methods in my controller , wallet
and generateWallet
user will call wallet
method via http request, it will check the database for wallet and if not exists it will call another function generateWallet
which is supposed to be a private function to generate a new wallet and thats when i get the error
here i my code
class DashboardController {
async wallet(req, res) {
let wallet = await UserWalletRepository.find({
user_id: req.authUser.id,
});
if (wallet) return res.send(wallet);
let newWallet = await this.generateWallet();
res.send(newWallet);
}
async generateWallet() {
let generatedWallet = await WalletService.generateWallet();
let newWallet = await UserWalletRepository.create({
user_id: req.authUser.id,
...generatedWallet,
});
return newWallet;
}
}
module.exports = new DashboardController();
here is the error
TypeError: this.generateWallet is not a function
at wallet (C:\node\first\app\controller\api\dashboard-controller.js:16:32)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
specifically this like
let newWallet = await this.generateWallet();
i assume something is changing this
keyword but i cant figure out what , there are routers and other codes involved in the stack but i dont think they have any effect since we are inside the function when we get this error ?
here is how i've set up my filder/files
so i have router
folder with api
and web
folder inside , containing route files for each section for example in routes/api/dashboard.js
i have
const DashboardController = require("../../app/controller/api/dashboard-controller");
const { AuthMiddleware } = require("../../app/middleware/auth");
module.exports = {
group: {
prefix: "/dashboard",
middleware: [AuthMiddleware],
},
routes: [
{
method: "get",
path: "/",
handler: DashboardController.index,
},
{
method: "get",
path: "/wallet",
handler: DashboardController.wallet,
},
],
};
and i have routes/inedx.js
which grabs all the route files and adds them to express
const express = require("express");
require("express-async-errors");
const webRoutes = require("./web/index");
const apiRoutes = require("./api/index");
class Router {
constructor() {
this.router = express.Router();
}
create(app) {
this._attachMiddleware();
this._attachWebRoutes();
this._attachApiRoutes();
app.use(this.router);
}
_attachMiddleware() {
this.router.use(express.json());
}
_attachWebRoutes() {
this._attachRoutes(webRoutes);
}
_attachApiRoutes() {
this._attachRoutes(apiRoutes, "/api");
}
_attachRoutes(routeGroups, globalPrefix = "") {
routeGroups.forEach(({ group, routes }) => {
routes.forEach(({ method, path, middleware = [], handler }) => {
this.router[method](
globalPrefix group.prefix path,
[...(group.middleware || []), ...middleware],
handler
);
});
});
}
}
module.exports = new Router();
i have server/index.js
const express = require('express')
const Router = require('../router/index')
class Server {
constructor(port){
this.port = port ;
this.app = express();
}
start(){
this._setupRoutes();
this._listin();
}
_listin(){
this.app.listen(this.port , ()=>{
console.log('app running on port' , this.port);
})
}
_setupRoutes(){
Router.create(this.app)
}
}
module.exports = Server ;
and finally my index.js
const Server = require('./server/index');
const app = new Server(8080);
app.start();
CodePudding user response:
Problem you are having is related to how you assign the DashboardController.wallet
to the handler which makes this
to be unbound:
{
method: "get",
path: "/wallet",
handler: DashboardController.wallet,
}
Since you don't have class variables, you can make your methods static and not use this
at all:
class DashboardController {
static async wallet(req, res) {
let wallet = await UserWalletRepository.find({
user_id: req.authUser.id,
});
if (wallet) return res.send(wallet);
let newWallet = await DashboardController.generateWallet();
res.send(newWallet);
}
static async generateWallet() {
let generatedWallet = await WalletService.generateWallet();
let newWallet = await UserWalletRepository.create({
user_id: req.authUser.id,
...generatedWallet,
});
return newWallet;
}
}
module.exports = DashboardController;
alternatively, you can use .bind()
to set the this
explicitly:
{
method: "get",
path: "/wallet",
handler: DashboardController.wallet.bind(DashboardController),
}