PreloadJS + Typescript - this

I have been stuck for 4 days so it is going on SO!
Please reference the PreloadJS Definitions:
https://github.com/borisyankov/DefinitelyTyped/blob/master/preloadjs/preloadjs.d.ts
From line 26.
My question is, how do you addEventListener in typescript while also binding it to the class scope?
imagine (semi psudo):
class Test
{
private _loader:createJS.AssetLoader;
constructor()
{
this._loader = new createJS.AssetLoader();
this._loader.addEventListener(???????????);
this._loader.load();
}
onLoadComplete(???): ???
{
}
}

Using TypeScript 0.9.1.0:
class Test
{
private _loader: createJS.AssetLoader;
constructor()
{
this._loader = new createJS.AssetLoader();
this._loader.addEventListenerthis.onLoadComplete);
this._loader.load();
}
onLoadComplete = () =>
{
console.log(this.whatever);
};
}
The important part here is using the member initialization syntax (member = value) for onLoadComplete and using a fat arrow lambda expression (() => { ... }) for the function. This will correctly capture the this context in the callback.

Related

How to add events to a derived/child class in NodeJS?

I have a child class which extends parent class. I also want child class to be able to emit events. Examples I found were extending EventEmitter. But JS does not allow multiple inheritance. I tried to implement it via mixin but not sure how to do without converting my parent class to a mixin.
Example
Say there is a base class for vehicles
all vehicles move and some can emit events when they move
class Vehicle {
constructor(name) { this.name = name }
move() { console.log(this.name, 'Moved'); }
}
class Car extends Vehicle {
constructor() { super('Ford'); }
move() {
super.move();
this.engineSound();
}
engineSound() {
console.log("Vroooom!");
}
// some other car related properties
}
class Ambulance extends Vehicle { // want to extend EventEmitter aswell
constructor() { super('Ambulance1'); }
move() {
super.move();
// emit moved event here
}
}
let normalVehicle = new Vehicle('Normal');
normalVehicle.move();
let car = new Car();
car.move();
let ambulance = new Ambulance();
// Listen for moving ambulance here
// ambulance.on('moved', () => { console.log('Ambulance coming') });
ambulance.move();
But to emit events docs suggest extending EventEmitter but JS does not support extending multiple classes.
Saw few examples which were using Object.assign but not sure how that can be applied here.
es6 class syntax doesn't support multiple inheritance at all. You can try weird trickery if you convert all classes to es5 constructor functions and then use things like Object.assign. But the best approach would be to use composition over inheritance.
OOP is used to try and mimic real world objects, so in real world terms it also doesn't make sense for an ambulance to be itself an event emitter.
However, using a composition approach, it makes sense for an ambulance to have an event emitter, like a radio or other such transmission device:
class Ambulance extends Vehicle {
#radio = new EventEmitter();
constructor() {
super("Ambulance");
}
emit(event) { this.#radio.emit(event) }
on(event, handler) { this.#radio.on(event, handler) }
move() {
super.move();
this.emit("moved");
}
}
let ambulance = new Ambulance();
ambulance.on('moved', () => { console.log('Ambulance coming') });
ambulance.move();
composition example
Note that the radio is private (using the new JS private fields feature), and I only supply a facade for the exact functionality I want the ambulance users to have, in this case 'on' and 'emit'.
With that said...
Lets get crazy and actually answer your question! Again, this will require to use es5 constructor functions:
// Base class - no inheritance
var Vehicle = (function () {
function Vehicle(name) {
this.name = name;
}
Vehicle.prototype.move = function () {
return this.name + " Moved\n";
};
return Vehicle;
})();
// Derived from Vehicle - regular inheritance from a single object
var Car = (function (_super) {
function Car() {
// Calling super() in constructor
_super.call(this, "Ford");
}
// extend Vehicle
Car.prototype = Object.create(Vehicle.prototype);
// bring back Car's constructor, because last step overrid it
Object.defineProperty(Car.prototype, "constructor", {
value: Car,
enumerable: false,
writable: true,
configurable: true
});
Car.prototype.move = function () {
return _super.prototype.move.call(this) + this.engineSound();
};
Car.prototype.engineSound = function () {
return "Vroooom!\n";
};
return Car;
})(Vehicle);
// Derived from Vehicle and EventEmitter - multiple inheritance!
var Ambulance = (function (_superVehicle, _superEventEmitter) {
function Ambulance() {
_superVehicle.call(this, "Ambulance");
_superEventEmitter.call(this);
}
// Create joint prototype using Object.assign
var jointPrototype = Object.assign(
{},
_superEventEmitter.prototype,
_superVehicle.prototype
);
// Connecting Ambulance to prototype chain
Ambulance.prototype = Object.create(jointPrototype);
// restore Ambulance's constructor
Object.defineProperty(Ambulance.prototype, "constructor", {
value: Ambulance,
enumerable: false,
writable: true,
configurable: true
});
Ambulance.prototype.move = function () {
this.emit("moved");
return _superVehicle.prototype.move.call(this);
};
return Ambulance;
})(Vehicle, EventEmitter);
es5 multiple inheritance example

Can I overload method in module.export in node.js?

I have an app.js with this code:
var addnote = (title,body) => { /* enter code here */ }
module.exports = {addnote};
Can I add another addnotes function with different parameters to that file?
Function overloading in JavaScript does not exist like in other programming languages such as C# and Java.
What you should be looking to do is pass an object as a parameter that has properties attached and filter them out there..
You could call different functions from your little 'mapping function' just implement the logic there if it isn't big (to keep the code clear).
function foo(parameters){
var title = parameters.title;
var body = parameters.body;
if(parameters.extraProperty){
// oh we have extraProperty passed in too, run a different function?
bar(title, body, parameters.extraProperty); // ??
}
}
foo({title: 'Title', body: 'Body', extraProperty: 'This is extra...'});
If this is your own custom module, you can use the concept of function overriding, where each child class can have its own way to handle something and also have a default way to do things.
class Parent {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello ${this.name}`);
}
}
class Child1 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hey there ${this.name}. This is Child 1`);
}
}
class Child2 extends Parent {
constructor(name) {
super(name);
}
greet() {
console.log(`Hi there ${this.name}. This is Child 2`);
}
}
const o1 = new Child1('Foo')
const o2 = new Child2('Foo')
o1.greet();
o2.greet();
But if you are trying to override a function in an external module(You do not have access to that code, like a library), my suggestion is to create a wrapper and add functionality there.

How to call remove listener on EventEmitter in nodejs?

I have below code to add listeners on EventEmitter,
class MyClass {
init() {
this.listener = new EventEmitter();
this.listener.on('eventName', this.onChange.bind(this));
}
onChange() {
...
}
}
How can I remove the listener from EventEmitter? I can see two different ways:
this.listener.removeListener('eventName', this.onChange)
this.listener.removeListener('eventName', this.onChange.bind(this))
I wonder which one I should use. Whether I need the bind when I remove it?
You can use bind() or you can use an Arrow Function in Node. Arrow Functions will inherit their execution context from the invoking context, which is similar to how bind() works and provides the same functionality.
this.listener.removeListener('eventName', this.onChange)
The above way of removing a listener won't work if it is being called in a removeListener(eventName) style function like the following:
const obj = new MyObject()
obj.init()
// This would blow up with a
// ReferenceError saying you cant call removeListener() on undefined
obj.listener.removeListener('event')
Not sure the use of the init() function when you can use a constructor with the class syntax.
constructor() {
this.listener = new EventEmitter()
this.boundEventNameHandler = () => this.onChange()
this.listener.on('eventName', this.boundEventNameHandler)
}
You can utilize the bound function for removing a listener within the context of a class using this.
let boundEventNameHandler = this.boundEventNameHandler
this.listener.removeListener('eventName', this.boundEventNameHandler)`
An example of this implemented looks like the following
const EventEmitter = require('events')
class MyObject {
constructor () {
this.listener = new EventEmitter()
this.boundEventNameHandler = () => this.onChange()
this.listener.on('eventName', this.boundEventNameHandler)
}
// I would reconsider making this a property of the MyObject class
// instead, make onChange() a local function outside the MyObject
// class definition because onChange in this example is only used by the
// event listener for 'eventName'
onChange () {
console.log('event fired change')
}
removeListeners () {
let boundEventNameHandler = this.boundEventNameHandler
this.listener.removeListener('eventName', boundEventNameHandler)
}
}
For TypeScript / ESNext you can use this:
class MyClass {
constructor() {
this.emitter = new EventEmitter();
this.emitter.on("event1", this.listener1);
this.emitter.on("event2", this.listener2);
this.emitter.removeListener("event1", this.listener1);
this.emitter.removeListener("event2", this.listener2);
this.emitter.emit("event1"); // no output
this.emitter.emit("event2"); // no output
}
public listener1 = (e) => {
console.dir(`listener1`);
};
public listener2 = (e) => {
console.dir(`listener2`);
};
}
Basically you define properties and instantly assign bound functions to them.
This way you use the same function reference in .on and .removeListener.

Get path of required module in NodeJS

I have 2 classes, one that extends from the other. I need to have a method that can be called from extending classes but resolves the path to reflect whatever directory the extending class module is in.
/main.js
class Main {
getTemplate () {
return readFileSync(__dirname + './template.ejs')
}
}
/some/path/module.js (also contains /some/path/template.ejs)
class Module extends Main {
}
In main code
const m = new Module()
m.getTemplate() // /some/path/template.ejs
The above doesn't work since __dirname doesn't reflect the 'module.js' path.
I've played around with require.resolve but I am not sure how I can use this from within the module.
/main.js
class Main {
constructor(dirname) {
this.dirname = dirname;
}
getTemplate () {
return readFileSync(this.dirname + './template.ejs')
}
}
/some/path/module.js (also contains /some/path/template.ejs)
class Module extends Main {
constructor() {
super(__dirname);
}
}
Ok. I've got a working alas hokey solution.
class Main {
constructor () {
try {
throw new Error()
} catch(e) {
const m = e.stack.match(/at new \w+ \(([^:]+)/)
this.dirname = path.dirname(m[1])
}
}
getTemplate () {
return readFileSync(this.dirname + '/template.ejs')
}
}
class Module extends Main {
}

With TypeScript: unable to refer to 'this' (class) from inside a function

I'm learning TypeScript and have the following class:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(getCertificate)
.fail(somethingWrong);
function getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
}
As you can see on the above code, the first call to this refers to my class DetailDriver. That's good. The second call to this (inside getCertificate) refers to any. That's not what I need. I need to refer to my class DetailDriver.
How to proceed?
Thanks.
Well,
According to section 4.9.2 of the TypeScript Language Specification you should use fat arrow syntax to preserve the scoping for this.
return promise
.then(() => return.this.id;)
.fail(somethingWrong);
Then the this keyword is properly determined to be a Driver.
For reference, you could also just do:
class SomeClass {
public someMethod() {
// Do something
}
public anotherMethod() {
var that = this; // Reference the class instance
function someFunction () {
that.someMethod();
}
}
}
You could refactor to something like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(this.getCertificate.bind(this)) // <- important part
.fail(somethingWrong);
}
// new method function here
private getCertificate() {
var id = this.driver.id(); // this refers to any
return ...
}
}
Using the function keyword anywhere in your class will make any reference to this keyword refer to that function rather than the outer class. Generally, you want to avoid defining functions inside of classes, unless you use the "fat arrow" syntax. That would look like this:
class DetailDriver {
public get driver() {
return super.getEntity();
}
public activate(): breeze.Promise {
var id = this.driver.id(); // this refers to (class) DetailDriver
return promise
.then(() => { // <- important part
var id = this.driver.id(); // this refers to any
return ...
})
.fail(somethingWrong);
}
}

Resources