4.5 KiB
title, description, published, authors, tags, attached, license
| title | description | published | authors | tags | attached | license | ||||
|---|---|---|---|---|---|---|---|---|---|---|
| Share Lifecycle Methods in Angular using Base Classes | 2022-09-13T22:12:03.284Z |
|
|
cc-by-4 |
Before we go on please note that this method of extending lifecycle methods is generally frowned upon by Angular experts. Instead, it's suggested to use a per-component dependency injection provided class instance with functions you call manually.
I write about this more in my upcoming free book called "The Framework Field Guide", which teaches React, Angular, and Vue all at once.
One downside is that you must add a declaration of the BaseComponent into your root NgModule. Otherwise, you'll end up with the following error during compilation:
BaseComponent is not declared in any Angular module
Luckily, since Angular 10 you can now use @Injectable to declare your BaseComponent instead. This sidesteps the problem because Injectables do not need to be declared:
@Injectable()
class BaseComponent implements OnInit {
ngOnInit() {
console.log('I AM BASE COMPONENT');
}
}
@Injectable()
class BaseComponent implements OnInit {
ngOnInit() {
console.log('I AM BASE COMPONENT');
}
}
@Component({
selector: 'app-root',
template: `
<p>Test</p>
`,
})
class AppComponent extends BaseComponent {
}
Overwriting Lifecycle Methods
@Injectable()
class BaseComponent implements OnInit {
ngOnInit() {
console.log('I AM BASE COMPONENT');
}
}
@Component({
selector: 'app-root',
template: `
<p>Test</p>
`,
})
class AppComponent extends BaseComponent implements OnInit {
override ngOnInit() {
super.ngOnInit();
}
}
Merging with Dependency Injection
import {Component, Inject, Injectable, NgModule, OnInit} from '@angular/core';
import {DOCUMENT} from "@angular/common";
@Injectable()
class BaseComponent implements OnInit {
constructor(@Inject(DOCUMENT) private document: Document) {}
ngOnInit() {
console.log(document.title);
}
}
@Component({
selector: 'app-root',
template: `
<p>Test</p>
`,
})
class AppComponent extends BaseComponent implements OnInit {
}
Overwriting constructor
TS2554: Expected 1 arguments, but got 0.
app.module.ts(7, 15): An argument for 'document' was not provided.
@Component({
selector: 'app-root',
template: `
<p>Test</p>
`,
})
class AppComponent extends BaseComponent implements OnInit {
// This code doesn't work. Read on to learn why
constructor(@Inject(DOCUMENT) private document: Document) {
super(document);
console.log(document.body);
}
}
Similar to how we had to add override to our AppComponent's lifecycle methods, we need to do the same with our constructor. Otherwise, we'll see the following error:
TS4115: This parameter property must have an 'override' modifier because it overrides a member in base class 'BaseComponent'.
Let's update the code to show what that might look like:
@Injectable()
class BaseComponent implements OnInit {
constructor(@Inject(DOCUMENT) private document: Document) {}
ngOnInit() {
console.log(document.title);
}
}
@Component({
selector: 'app-root',
template: `
<p>Test</p>
`,
})
class AppComponent extends BaseComponent implements OnInit {
constructor(@Inject(DOCUMENT) private override document: Document) {
super(document);
console.log(document.body);
}
}
While this might appear to work at first, you'll quickly find a compiler error with the following code:
TS2415: Class 'AppComponent' incorrectly extends base class 'BaseComponent'.
Types have separate declarations of a private property 'document'.
To solve this, we simply need to make our BaseComponent's constructor properties public instead of private:
@Injectable()
class BaseComponent implements OnInit {
constructor(@Inject(DOCUMENT) public document: Document) {}
ngOnInit() {
console.log(document.title);
}
}
@Component({
selector: 'app-root',
template: `
<p>Test</p>
`,
})
class AppComponent extends BaseComponent implements OnInit {
constructor(@Inject(DOCUMENT) public override document: Document) {
super(document);
console.log(document.body);
}
}
Remember to keep your
overrideproperty in theAppComponentconstructor, otherwise you'll have errors.