mirror of
https://github.com/LukeHagar/unicorn-utterances.git
synced 2025-12-09 21:07:49 +00:00
WIP initial outline
This commit is contained in:
committed by
Corbin Crutchley
parent
233f4aa6af
commit
77c7151f6d
218
content/blog/angular-extend-class/index.md
Normal file
218
content/blog/angular-extend-class/index.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
title: "Share Lifecycle Methods in Angular using Base Classes",
|
||||||
|
description: "",
|
||||||
|
published: '2022-09-13T22:12:03.284Z',
|
||||||
|
authors: ['crutchcorn'],
|
||||||
|
tags: ['angular', 'javascript', 'webdev'],
|
||||||
|
attached: [],
|
||||||
|
license: '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](https://framework.guide).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 `Injectable`s do not need to be declared:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
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.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@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:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@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`:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
@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 `override` property in the `AppComponent` `constructor`, otherwise you'll have errors.
|
||||||
Reference in New Issue
Block a user