Don't invoke inherited method twice in ES6 classes - javascript

I'm moving from RequireJS to browserify (together with babelify) and try to rewrite my current modules to classes. For each of my RequireJS modules I have a method called eventHandler which handles all module specific events. Now when I extend a class, the parent class calls the subclass`s eventHandler method which leads to invoking the method twice.
Parent class:
'use strict';
class Tooltip {
constructor() {
this.eventHandler();
}
eventHandler() {
// Module specific events
}
}
module.exports = Tooltip;
Subclass:
'use strict';
import Tooltip from './Tooltip';
class Block extends Tooltip {
constructor() {
super();
this.eventHandler();
}
eventHandler() {
// Module specific events
// Gets called twice
}
}
module.exports = Block;
I liked the fact that the eventHandler method was named the same across all modules as it was easier to maintain. That's why I'd like to keep this pattern. So what would be the best way to solve this problem? Thanks for any suggestions!

Since you know the parent constructor calls this.eventHandler, don't do so in the derived classes:
'use strict';
import Tooltip from './Tooltip';
class Block extends Tooltip {
constructor() {
super();
// (No call here)
}
eventHandler() {
// Module specific events
// Gets called twice
}
}
module.exports = Block;
Re your comment:
The parent classes don't always implement an eventHandler method. So I need to ensure it gets called in this case as well.
Where they do, don't do the call. Where they don't, do do the call. Subclasses are very tightly bound to superclasses, by their nature.
If you can assume (!) that the presence of eventHandler in the super means it has called it from its constructor, you could do something like this:
constructor() {
super();
if (!super.eventHandler) {
this.eventHandler();
}
}
...but again, the nature of the super/sub relationship is that it's very tightly-bound, so relying on your knowledge of whether the super does this or not is reasonable.

Related

In two ES6 Parent-Child classes, is there a way to execute some code in parent class immediately after child constructor executes?

Example:
class Parent { constructor() {} }
class Child { constructor() { super(); someChildCode(); } }
I just want to execute some code after someChildCode(). Yes, I can place it there, but requirement is not placing that code there.
Because there are too many (n) child classes, and only one Parent, thus I want to not duplicate code (n) times.
P.S. I want clean code solution as simple as new Child() when creating child objects.
P.S. Downvoter, care to explain? I realise that the task may not be solvable, that is why I have asked the question, to be sure if that's the case.
If you really need the exact same code to run in every child class upon construction, then that common code probably belongs in the parent constructor. But maybe you need some child-specific code to run before the common code, in which case you still have an issue.
Either way, one possible solution would be not to override the constructor in the child classes at all, but instead let them inherit the parent constructor, which contains any common code as well as pre- and post- hooks you can override in child classes as necessary. For example:
class Parent {
// do not override the constructor, conceptually "final"
constructor() {
this.beforeCommonConstructorCode();
// add any common constructor code here
console.log("Common constructor code")
this.afterCommonConstructorCode();
}
// override this in child classes as needed
public beforeCommonConstructorCode() {
// empty
}
// override this in child classes as needed
public afterCommonConstructorCode() {
// empty
}
}
new Parent();
// in console:
// >> Common constructor code
And when you subclass Parent, you leave the constructor alone and add code to the appropriate methods:
class Child extends Parent {
// remember, do not override constructor
public beforeCommonConstructorCode() {
console.log("Child pre-constructor code")
}
public afterCommonConstructorCode() {
console.log("Child post-constructor code")
}
}
new Child();
// in console:
// >> Child pre-constructor code
// >> Common constructor code
// >> Child post-constructor code
Note that TypeScript will not prevent child classes from overriding the constructor (there is no "final" keyword or functionality) so discipline is needed. Otherwise, I think this behaves the way you like; you are freed from having to place common code in each subclass.
Hope that helps. Good luck.
If I understand this correctly, you can have an intermediate parent to achieve this given your code is generic and does not depend on child.
class Parent(){
}
class InterMediateParent extends Parent{
constructor(){
super();
someCode();
}
}
class Child extends InterMediateParent{
constructor(){
super();
}
}
You can add that function to the Parent prototype so that only one copy is maintained across all child objects and call that function in child's constructor.
class Parent {
constructor() {
}
}
Parent.prototype.someCommonCode = function() {
console.log("Common code for all Parent as well as it's child objects");
}
class Child extends Parent {
constructor() {
//Child class code
super();
//Some child code
this.someChildCode();
//Call Parent's common function
super.someCommonCode();
}
someChildCode() {
console.log("Some child code");
}
}
let child1 = new Child();
let child2 = new Child();

Is it possible to create a global attach() handler for viewmodels?

I have a use case with Aurelia where I would like to have a handler run for every view that is attached. (It's an HTML5 polyfill for date and number inputs that would work via querySelector.) I realize that I could call this within every view that I create, but I'm wondering if there's a best practice to set this at a global level. (Note: This could probably be done with a router pipeline step, but all views may not be subject to that, such as views loaded via compose.)
I realize that this could potentially be dangerous, but is there a best practice to add global attached() and detached() handlers for views and viewmodels?
Edit: Looking here (https://github.com/aurelia/templating/blob/ee5b9d6742fddf3d163aee8face6e6a58ba1554c/src/view.js#L259) it looks as though it would be possible to add a hook for a global handler here that took a view as an argument, but I'd rather not have to change the framework code if possible.
My idea would be to create a base viewmodel class with an attached logic, which would contain globally required functionality.
Extended viewmodels could call super.attached() to execute global logic as needed.
You can find a demo here: https://gist.run/?id=fea4069d8a4361c4802c7c5d42105145
This can work with compose as well. I know, it isn't a completely automated solution but an opt-in method, so it would require a bit of additional work on all viewmodels.
Base class - used by all viewmodels
import { inject } from 'aurelia-framework';
#inject(Element)
export class BaseView {
constructor(element) {
this.element = element;
}
attached() {
// global logic goes here
}
}
Example viewmodel - actual implementation
import { BaseView } from './base-view';
import { inject } from 'aurelia-framework';
#inject(Element)
export class ExtendedView extends BaseView {
constructor(element) {
super(element);
}
attached() {
super.attached(); // global logic runs
}
}

Can I use this.on to listen to events in EventEmitter? Looking for correct usage of Eventmitter in class in javascript

I am using EventEmitter3 which is an event module in my javascript code. It works just like the event module in Node.js. But I am facing problems to use it correctly.
Normally if I use the module in my code, I would create an object of the EventEmitter and then use object.emit to fire the events and object.on to listen to the events. And what I normally do is put object.on in another class so it works perfectly.
Now I try to organize my code in ES6 classes. So I extend EventEmitter in my customized class. Assume it is called ClassA, which is a subclass of EventEmitter. If I listen to its own event in the class definition, like this:
import EventEmitter from 'eventemitter3';
class ClassA extends EventEmitter {
constructor() {
...
}
method() {
this.emit('send');
}
this.on('send', function(data) {
// Do something with data
});
}
I am getting an Unexpected token error on this.on line. But I am not sure how to listen to the class's own event inside the class or is this even doable? Do I have to put the listeners outside of the class definition?
You need to put calls that are supposed to run for each instance inside the constructor:
class ClassA extends EventEmitter {
constructor() {
super()
…
this.on('send', data => {
// Do something with data
});
}
method() {
this.emit('send');
}
}
Otherwise let the creator of the instances listen to the events, or listen/unlisten from within your class's methods.

Cannot use child class' properties within parent class constructor via Babel's transform-class-properties

When extending a parent class and declaring the child class' properties via Babel's 'transform-class-properties' plugin, any class properties of the child class are not accessible via the parent class' constructor method.
class One {
internal = 1;
constructor() {
console.log('constructor internal', this.internal);
}
}
class Two extends One {
internal = 2;
}
new Two();
In the example above, 'constructor internal 1' will be output to the console. When looking at the compiled code this is obvious as to why, with the parent class being executed first and the resulting object then being integrated with the child class.
Apologies if this is by design, but it has confused me as the following code works in the way I am expecting within non-constructor methods (so the boot() method references the child class' 'internal' property value):
class One {
internal = 1;
constructor() {
console.log('constructor internal', this.internal);
}
boot() {
console.log('boot internal', this.internal);
}
}
class Two extends One {
internal = 2;
constructor() {
super();
this.boot();
}
}
new Two();
So, even when calling a method declared on the parent class, it will inherit the child class' properties. It is just constructor methods which seemingly do not behave as expected (at least by me - again, apologies if this is wrongly interpreted, but there are no caveats listed on the relative Babel page.)
Thank you.
I think that is natural. If you wish to override the parent class's property init value, you should do it in the derived class's constructor.
class Two extends One {
constructor() {
// Call parent constructor.
super();
// Override here.
this.internal = 2;
}
}
Hope it helps. Happy coding (:
loganfsmyth answered my question very clearly here: https://phabricator.babeljs.io/T7567 (Thanks!)
This is indeed expected behavior. Babel's implementation is a little incorrect because in a perfect world this would also throw an error, because you shouldn't have the same property defined in a parent and a child, but we don't do that right at the moment.
Class properties are initialized when:
For base classes, before the constructor executes
For child classes, at the end of super()
and when a given constructor initializes it's bindings, it uses that classes bindings, it doesn't know of anything in child classes. That means your One class knows the value should be 1, so that's what it sets.

Overidding library function in es6

I'm trying to override specific function in a library.
In my case, I'm trying to override some functions on Framework7. The library simply has class called Framework7, in non ES6 javascript, creating instance of application would look like this:
var app = new Framework7();
so I assume it's extendable, so here my code to extends it:
export class Application extends Framework7 {
constructor(options) {
super(options);
}
}
the code run fine, however, when I try to override one of the function, let say showPreloader, the function itself is never called
export class Application extends Framework7 {
constructor(options) {
super(options);
}
showPreloader(title) {
console.log('this not printed :(');
super(title); // this is not called as well
// but showPreloader() from Framework7 still called
}
}
I also try different approach to override it, i come with a solution like this:
export class Application extends Framework7 {
constructor(options) {
super(options);
this.showPreloader = (title) => {
console.log('print me!'); // printed! :D
super(); // oh, wait! cannot call super from here! :(
}
}
}
However, it looks a bit ugly and I cannot call super from there.
Is there any workaround so I can override a function from library and calling the base function via super (or anything?)
I assume it's extendable
Don't. Read the docs, ask the authors, or read the source yourself.
In your case, the library you've chosen doesn't exactly follow best practises, it just installs its methods directly on the app "instance". It's a factory function, not a constructor.
Is there any workaround so I can override a function from library and calling the base function?
Yes, by storing the original method in a variable before overwriting it. You then can call it using .call(this) (like inheritance was done in ES5).
…
const original = this.showPreloader;
this.showPreloader = (title) => {
console.log('print me!'); // printed! :D
original.call(this, title);
}
However, that's no fun, especially since it's not just a few instance-specific methods but actually all of them. So you'd better drop ES6 class syntax and "subclassing" here, and use a parasitical inheritance approach instead:
function MyFramework7(options) {
const app = new Framework7(options);
const {showPreloader, …} = app; // all the original methods
… // mess with the object to your liking
return app;
}
Or maybe you don't even need to wrap it in a function, as app is a singleton I guess.

Categories

Resources