I'm trying to migrate an old library of Angular components to standalone components.
A number of the components have a lot in common, so I use a superclass for all of the common behavior. At least as I was accustomed to doing things, the superclass of a @Component
shouldn't be another @Component
, but rather a @Directive
.
The start of the superclass looks like this:
// @dynamic
@Directive()
export abstract class DigitSequenceEditorDirective<T> implements
AfterViewInit, ControlValueAccessor, OnInit, OnDestroy, Validator {
// ...
And one the subclass components starts like this:
// @dynamic
@Component({
selector: 'tbw-time-editor',
animations: [BACKGROUND_ANIMATIONS],
templateUrl: '../digit-sequence-editor/digit-sequence-editor.directive.html',
styleUrls: ['../digit-sequence-editor/digit-sequence-editor.directive.scss', './time-editor.component.scss'],
providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TimeEditorComponent), multi: true },
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => TimeEditorComponent), multi: true }]
})
export class TimeEditorComponent extends DigitSequenceEditorDirective<number> implements OnInit {
// ...
The problem now, porting this to Angular 19 and trying to use a standalone design, is that the HTML for the directive contains some CommonModule features like [ngClass]
and [ngStyle]
. (There are also uses of *ngIf
and the like, but those I can get rid of using @if
, @for
, etc.)
A non-functional recommendation by Google AI for using CommonModule
features in a directive looks like this:
import { Directive, Input } from '@angular/core';
import { NgIf } from '@angular/common';
@Directive({
selector: '[appMyStandalone]',
standalone: true,
imports: [NgIf] // Can't be done! No `imports` field! (I'd have NgClass and NgStyle here if this worked)
})
export class MyStandaloneDirective {
@Input() appMyStandalone: boolean = false;
}
If I can't use imports
with a directive, how do I resolve the red squiggly line stuff below?
The directive never contains the imports used in HTML, it always exists on the parent component.
The content of the decorator is never inherited in angular, only the class properties and methods.
The reason we use @Directive({ ... })
is so that you can use angular features like model
, input
, etc.
So add the common module imports in the child component, the parent directive decorator contents are never used anyway.
// @dynamic
@Component({
selector: 'tbw-time-editor',
animations: [BACKGROUND_ANIMATIONS],
templateUrl: '../digit-sequence-editor/digit-sequence-editor.directive.html',
styleUrls: [
'../digit-sequence-editor/digit-sequence-editor.directive.scss',
'./time-editor.component.scss'
],
providers: [
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TimeEditorComponent), multi: true },
{ provide: NG_VALIDATORS, useExisting: forwardRef(() => TimeEditorComponent), multi: true }
],
imports: [
...
NgIf
...
],
})
export class TimeEditorComponent extends DigitSequenceEditorDirective<number> implements OnInit {
// ...