Angular 9 decorators on abstract base class

Viewed 6762

I'm working on upgrading my project from Angular 8 to 9, and I've come across a problem with new requirements when extending classes.

According to Angular's documentation:

Undecorated base classes using Angular features

As of version 9, it's deprecated to have an undecorated base class that:

  • uses Angular features
  • is extended by a directive or component

Angular lifecycle hooks or any of the following Angular field decorators are considered Angular features:

  • @Input()
  • @Output()
  • @HostBinding()
  • @HostListener()
  • @ViewChild() / @ViewChildren()
  • @ContentChild() / @ContentChildren()

For @Component decorators, it requires a template or templateURL on the base class. Adding either causes the child class to not render it's template.

For example, the following result in nothing rendering on the view:

@Component({
  template: ''
})
export abstract class BaseComponent<T extends AbstractSuperEntity> extends Toggler implements OnChanges {
  @Input()
  year: number | string

  constructor(service: MyService) {

  }

  ngOnChanges() {
  }
}

@Component({
  templateUrl: 'my.component.html',
  selector: 'my-component'
})
export class MyComponent extends BaseComponent<AbstractSuperEntity> {

  constructor(service: MyService) {
    super(service);
  }

}

I tried changing the base class to use templateUrl pointing to an empty html, but that doesn't work either.

2 Answers

You have to add an empty @Directive() decorator. As far as I know, that should be enough:

@Directive()
export abstract class BaseComponent<T extends AbstractSuperEntity> extends Toggler implements OnChanges {
  @Input()
  year: number | string

  constructor(service: MyService) {

  }

  ngOnChanges() {
  }
}

It's been quite a while after this question was asked, but I stumbled upon it so this might be useful to others as well.

You don't necessarily have to add the @Directive decorator. If it's a component just use @Component instead. That should work. See the following stackblitz example:

https://stackblitz.com/edit/angular-ivy-kehmwu?file=src/app/app.component.ts

In the question it's unclear what's wrong. An error message would help.

What also might've gone wrong is that the OP was not implementing the lifecycle method on the super call:

import { Component, OnInit, Input } from '@angular/core';

@Component({
  template: '',
})
export abstract class BaseComponent implements OnInit {
  @Input()
  year: number | string;

  constructor() {}

  ngOnInit() {
    console.log('This will only work if you call it in the child component');
  }
}

@Component({
  selector: 'child-component',
  template: '<p>test, year: {{ year }}</p>',
})
export class ChildComponent extends BaseComponent implements OnInit {
  constructor() {
    super();
  }

  ngOnInit() {
    super.ngOnInit();
  }
}
Related