In Aurelia if you have a parent/base class and one or more children that inherit from this class AND the parent class has some injectables via Aurelia’s dependency injection container, you have most likely encountered an issue with needing to import and pass the dependencies back to the parent.
You might have done something like this:
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
@inject(Router)
export class Parent {
constructor(router) {
this.router = router;
}
}
import {Parent} from './parent';
import {Router} from 'aurelia-router';
@inject(Router)
export class Child extends Parent {
constructor(router) {
super(router);
}
}
Gross.
Or maybe you imported the container directly and did this in your parent/base class:
import {Container} from 'aurelia-framework';
import {Router} from 'aurelia-router';
export class Parent {
constructor() {
this.router = Container.instance.get(Router);
}
}
A little bit better, but still, there is no need to directly access the DI container for such a thing.
What if I told you there was a better way? Let’s use the spread operator and rest parameters to get our parent dependencies:
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
@inject(Router)
export class Parent {
constructor(router) {
this.router = router;
}
}
import {Parent} from './parent';
export class Child extends Parent {
constructor(...rest) {
super(...rest);
}
}
Now what if you wanted to introduce a new dependency from within your child class? You can just do this:
import {inject} from 'aurelia-framework';
import {Router} from 'aurelia-router';
@inject(Router)
export class Parent {
constructor(router) {
this.router = router;
}
}
import {inject} from 'aurelia-framework';
import {Parent} from './parent';
import {MyService} from './my-service';
@inject(MyService)
export class Child extends Parent {
constructor(myService, ...rest) {
super(...rest);
this.myService = myService;
}
}
You gotta love Aurelia, sometimes it feels like it makes some things a little TOO EASY.
the real problem of VM inheritance are the bindables not being registered on children. Quite possibly the biggest headache when trying to have the most maintainable code.
I’ve tried doing something very similar and I get “Supplied parameters do not match any signature of call target” on the child calls to super(…rest). Any ideas?
Not working with Typescript. It seems like spread operator cannot be used with super(…rest) call in TS.
@petronellius
What version of TypeScript are you running? Support was added in a little while ago for rest/spread operators.
TypeScript 2.1. I am getting same error as dave. I found issue on github where some developer describes same problem with inheritance and spread operator.
https://github.com/Microsoft/TypeScript/issues/4130
(pfrankov commented on Dec 2, 2016)
Love this design pattern!
I don’t think this actually solves the problem.
In your Child class, try the following:
1) Remove the Router import
2) Remove the Router injection
In other words, simply provide a constructor signature and a super signature with a number of args matching the number of arguments in the Parent class’ constructor.
You will see that it still works. In other words, importing and injecting dependencies in the Child class and passing them to the Parent class appears to have never been necessary. It’s the signatures that matter.
Thanks, this worked well and much cleaner. (using CLI and requierJS)
Google never fails to amaze me! I literally googled “Aurelia Inheritance” and landed here on this beaut. Nice work, Sir.
Oh man, did this save a project I was planning. Thanks so much!
Fantastic! This works very well. Thank you!
Love your Aurelia for Real-World Applications book as well!
This still does not work in Typescript. I’m using typescript v2.7.x.