user2932053
user2932053

Reputation:

ExpressJS SubClasses Missing Inherited attributes and properties in Callbacks

I am using ExpressJS 4.16 on Node v8.9.4, and have created base controller that handles default routes. Here is an sample example:

class BaseController {
  constructor(name, app, router, data) {
    this._name = name;
    this._app = app;
    this._router = router;
    this._data = data;

  }

   get name() {
    return this._name;
  }

  setup_aggregate_routes() {
     //Add Top Level Routes (All Entities)
    this._router
       .route(this.aggregate_route) // route = {application_root}/{entity}s/
       .get(this.get_aggregate_request)
  }

  default_request(req, res) {
    res.status(400).json(this.not_implemented_message);
  }

  get aggregate_route() {
    return `/${this.name}s`;
  }
  get_aggregate_request(req, res) {
    this.default_request(req, res);
  }
}

The "this._router" is an "express.router()" object and the "this._app" is an express object. I then proceed to inherit from this class as so.

const BaseController = require("./base");
class CatalogController extends BaseController {
  get_aggregate_request(req, res) {
    console.log(this._data);
    console.log(this.name);
    res.status(200).json();
  }
}

However, when doing this when I am in the get_aggregate_request callback for the subclass "CatalogController", "this" is undefined. I then proceed to replace "this" with "super" but the properties such as "_data" are not available and "name" getter is called but when it uses this._name it is not available.

I am stumped as to why. Any assistance to properly wire this up would be appreciated.

EDIT Here is an example of how super is being used:

const BaseController = require("./base");
class CatalogController extends BaseController {
  get_aggregate_request(req, res) {
    console.log(super._data);
    console.log(super.name);
    res.status(200).json();
  }
}

Here is an example of how the class is instantiated:

const catalog_controller = new CatalogController(
  "catalog",
  app,
  express.Router(),
  data.catalog
);

** Additional information ** In order to address this problem I simplified the code and removed Express from the equation

Base Controller:

class BaseController {
  constructor(
    name, data
  ) {
    this._name = name;
    this._data = data;
  }

  get name() {
    return this._name;
  }

  setup_aggregate_routes() {
    //Add Top Level Routes (All Entities)
    this.example_test_function(null, this._data, this.callback)
  }

  example_test_function(err, data, cb){
    cb(data);
  }

  callback(data){
    console.log(`${this.name}: ${JSON.stringify(data)}`);
  }

  default_request(req, res) {
    res.status(400).json(this.not_implemented_message);
  }

  get_aggregate_request(req, res) {
    this.default_request(req, res);
  }

}
module.exports = BaseController;

Catalog Controller (inherited):

const BaseController = require("./base");

class CatalogController extends BaseController {
  callback(data) {
    console.log(`${this.name}: ${JSON.stringify(data)}`);
  }
}

module.exports = CatalogController;

Driver:

const BaseController = require("./base")
const base_controller = new BaseController("base",
{ a:"abc" }
)

base_controller.setup_aggregate_routes()

const CatalogController = require("./catalog");

const catalog_controller = new CatalogController(
    "catalog",
   { a:"abc" }
  );

catalog_controller.setup_aggregate_routes()

Since ES2015/ES6 does not require an explicit call to the super constructor in a subclass constructor if the signatures are the same. The subclass ends up using the base (super) class constructor. So this is not an inheritance problem. I then tested the above and ran into a similar problem. It is not a problem with Inheritance or Express. It is the old javascript context problem and using "this".

This is identified here. How to access the correct `this` inside a callback?

Based on the last identified sample, if I change the code based on one of the identified solutions:

from:

cb(data);

to:

cb.bind(this)(data);

It works. I will provide a complete solution using ExpressJS as well

Upvotes: 1

Views: 237

Answers (2)

user2932053
user2932053

Reputation:

The solution to this problem is associated to context, callbacks and the use of "this" and not an inheritance problem nor Express problem. It is identified here:

How to access the correct `this` inside a callback?

If I was using my original example to correct this problem, the only change I would need to apply is the following:

Change this:

.get(this.get_aggregate_request)

To this:

.get(this.get_aggregate_request.bind(this))

Of course there are other ways to resolve the context problem as identified in the article but that is ultimately up to you.

Upvotes: 1

Rahul Sharma
Rahul Sharma

Reputation: 10111

try this

class CatalogController extends BaseController {

    constructor(name, app, router, data) {
        super(name, app, router, data);
    }

    get_aggregate_request(req, res) {
        //super(req, res); // if you want to call
        res.status(200).json();
    }
}

Upvotes: 0

Related Questions