A simpler Angular component inheritance

Related to this documentation in Angular website: https://angular.dev/guide/components/inheritance Simplifying inheritance by removing the need for calling super for: common dependency injection common lifecycle hooks Stackblitz link: https://stackblitz.com/edit/stackblitz-starters-pcp95vzm?file=src%2Fmain.ts import { Component, ElementRef, inject, Input } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; @Component({ selector: 'base-listbox', template: ` Base Listbox `, host: { '(keydown)': 'handleKey($event)', }, }) export class ListboxBase { @Input() value: string = ''; protected isInitialized = false; protected element = inject(ElementRef); ngOnInit() { this.isInitialized = true; console.log('ngOnInit'); this.onInit(); } protected onInit() {} handleKey(event: KeyboardEvent) { console.log('handleKey'); } } @Component({ selector: 'custom-listbox', template: ` Custom Listbox `, host: { '(click)': 'focusActiveOption()', }, }) export class CustomListbox extends ListboxBase { @Input() disabled = false; focusActiveOption() { console.log('focusActiveOption'); } override onInit() { console.log('onInit'); } } @Component({ selector: 'app-root', template: ` Hello from {{ name }}! Learn more about Angular `, imports: [CustomListbox, ListboxBase], }) export class App { name = 'Angular'; } bootstrapApplication(App); Advice is to have: { "compilerOptions": { "noImplicitOverride": true, } } in tsconfig if not already there. Goal is to avoid dev to mistakenly write an ngOnInit without knowing they'd be overriding the parent one.

Apr 5, 2025 - 06:09
 0
A simpler Angular component inheritance

Related to this documentation in Angular website:

https://angular.dev/guide/components/inheritance

Simplifying inheritance by removing the need for calling super for:

  • common dependency injection
  • common lifecycle hooks

Stackblitz link:

https://stackblitz.com/edit/stackblitz-starters-pcp95vzm?file=src%2Fmain.ts


import { Component, ElementRef, inject, Input } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'base-listbox',
  template: `
    Base Listbox
  `,
  host: {
    '(keydown)': 'handleKey($event)',
  },
})
export class ListboxBase {
  @Input() value: string = '';

  protected isInitialized = false;

  protected element = inject(ElementRef);

  ngOnInit() {
    this.isInitialized = true;
    console.log('ngOnInit');
    this.onInit();
  }

  protected onInit() {}

  handleKey(event: KeyboardEvent) {
    console.log('handleKey');
  }
}

@Component({
  selector: 'custom-listbox',
  template: `
    Custom Listbox
  `,
  host: {
    '(click)': 'focusActiveOption()',
  },
})
export class CustomListbox extends ListboxBase {
  @Input() disabled = false;
  focusActiveOption() {
    console.log('focusActiveOption');
  }

  override onInit() {
    console.log('onInit');
  }
}

@Component({
  selector: 'app-root',
  template: `
    

Hello from {{ name }}!

Learn more about Angular `
, imports: [CustomListbox, ListboxBase], }) export class App { name = 'Angular'; } bootstrapApplication(App);

Advice is to have:

{
  "compilerOptions": {
    "noImplicitOverride": true,
  }
}

in tsconfig if not already there.
Goal is to avoid dev to mistakenly write an ngOnInit without knowing they'd be overriding the parent one.