I would like to create extensions for some components already deployed in Angular 2, without having to rewrite them almost completely, as the base component could undergo changes and wish these changes were also reflected in its derived components.
I created this simple example to try to explain better my questions:
With the following base component app/base-panel.component.ts
:
import {Component, Input} from 'angular2/core';
@Component({
selector: 'base-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel{
padding: 50px;
}
`]
})
export class BasePanelComponent {
@Input() content: string;
color: string = "red";
onClick(event){
console.log("Click color: " + this.color);
}
}
Would you like to create another derivative component only alter, for example, a basic component behavior in the case of the example color, app/my-panel.component.ts
:
import {Component} from 'angular2/core';
import {BasePanelComponent} from './base-panel.component'
@Component({
selector: 'my-panel',
template: '<div class="panel" [style.background-color]="color" (click)="onClick($event)">{{content}}</div>',
styles: [`
.panel{
padding: 50px;
}
`]
})
export class MyPanelComponent extends BasePanelComponent{
constructor() {
super();
this.color = "blue";
}
}
Complete working example in Plunker
Note: Obviously this example is simple and could be solved otherwise no need to use inheritance, but it is intended only to illustrate the real problem.
As you can see in the implementation of the derivative component app/my-panel.component.ts
, much of the implementation was repeated, and the single part really inherited was the class
BasePanelComponent
, but the @Component
had to basically be completely repeated, not just the changed portions, as the selector: 'my-panel'
.
Is there some way to make a literally full inheritance of a component Angular2, inheriting the class
definition of the markings/annotations, as for example @Component
?
Edit 1 - Feature Request
Feature request angular2 added to the project on GitHub: Extend/Inherit angular2 components annotations #7968
Edit 2 - Closed Request
The request was closed, for this reason, that briefly would not know how to merge the decorator will be made. Leaving us with no options. So my opinion is is quoted in the Issue.
As far as I know component inheritance has not been implemented yet in Angular 2 and I'm not sure if they have plans to, however since Angular 2 is using typescript (if you've decided to go that route) you can use class inheritance by doing
class MyClass extends OtherClass { ... }
. For component inheritance I'd suggest getting involved with the Angular 2 project by going to https://github.com/angular/angular/issues and submitting a feature request!Now that TypeScript 2.2 supports Mixins through Class expressions we have a much better way to express Mixins on Components. Mind you that you can also use Component inheritance since angular 2.3 (discussion) or a custom decorator as discussed in other answers here. However, I think Mixins have some properties that make them preferable for reusing behavior across components:
I strongly suggest you read the TypeScript 2.2 announcement above to understand how Mixins work. The linked discussions in angular GitHub issues provide additional detail.
You'll need these types:
And then you can declare a Mixin like this
Destroyable
mixin that helps components keep track of subscriptions that need to be disposed inngOnDestroy
:To mixin
Destroyable
into aComponent
, you declare your component like this:Note that
MixinRoot
is only necessary when you want toextend
a Mixin composition. You can easily extend multiple mixins e.g.A extends B(C(D))
. This is the obvious linearization of mixins I was talking about above, e.g. you're effectively composing an inheritnace hierarchyA -> B -> C -> D
.In other cases, e.g. when you want to compose Mixins on an existing class, you can apply the Mixin like so:
However, I found the first way works best for
Components
andDirectives
, as these also need to be decorated with@Component
or@Directive
anyway.Alternative Solution:
This answer of Thierry Templier is an alternative way to get around the problem.
After some questions with Thierry Templier, I came to the following working example that meets my expectations as an alternative to inheritance limitation mentioned in this question:
1 - Create custom decorator:
2 - Base Component with @Component decorator:
3 - Sub component with @CustomComponent decorator:
Plunkr with complete example.
Angular 2 version 2.3 was just released, and it includes native component inheritance. It looks like you can inherit and override whatever you want, except for templates and styles. Some references:
New features in Angular 2.3
Component Inheritance in Angular 2
A Plunkr demonstrating component inheritance
I know this doesn't answer your question but I really think inheriting / extending components should be avoided. Here's my reasoning:
If the abstract class extended by two or more components contains shared logic: use a service or even create a new typescript class that can be shared between the two components.
If the abstract class... contains shared variables or onClicketc functions, Then there will be duplication between the html of the two extending components views. This is bad practice & that shared html needs to be broken into Component(s). These Component(s) (parts) can be shared between the two components.
Am I missing other reasons for having an abstract class for components?
An example I saw recently was components extending AutoUnsubscribe:
this was bas because throughout a large codebase, infiniteSubscriptions.push() was only used 10 times. Also importing & extending AutoUnsubscribe actually takes more code than just adding mySubscription.unsubscribe() in the ngOnDestroy() method of the component itself, which required additional logic anyway.