Angular - Inject TemplateRef inherited from base component

Discussão em 'Angular' iniciado por Cody Pritchard, Fevereiro 23, 2025.

  1. I am trying to create some common UI components while using directive composition in Angular 19.

    I have a base abstract directive that defines some initial classes and inputs:

    @Directive({
    selector: '[abstractButton]',
    })
    export class AbstractButtonDirective {
    protected readonly _elementRef: ElementRef<HTMLButtonElement> = inject(ElementRef);

    readonly type = input<ButtonTypes>('button');
    readonly size = input<ButtonSizes>('sm');
    readonly color = input<ButtonColors | StateColors>();
    readonly shape = input<ButtonShapes>();
    ...

    }


    I have another abstract component that uses this as a host directive and defines a template:

    @Component({
    selector: '[abstractActionButton]',
    imports: [CommonModule],
    template: `
    <ng-template
    #buttonTpl
    let-icon="icon"
    let-label="label"
    >
    <div class="flex items-center gap-2">
    <span>
    <i class="fa-lg fa-light {{ icon }}"></i>
    </span>
    <span
    [class.hidden]="responsive() || compact()"
    [class.md:block]="responsive() && !compact()"
    >
    {{ label }}
    </span>
    </div>
    </ng-template>
    `,
    hostDirectives: [{
    directive: AbstractButtonDirective,
    inputs: ['size', 'joined']
    }],
    changeDetection: ChangeDetectionStrategy.OnPush,
    }) export class AbstractActionButtonComponent {
    private readonly _buttonTplRef = viewChild.required<TemplateRef<{icon: string, label: string}>>('buttonTpl');

    get buttonTpl() {
    return this._buttonTplRef();
    }

    readonly responsive = input(false, {
    transform: (value: boolean | string) =>
    typeof value === 'string' ? value === '' : value,
    });

    readonly compact = input(false, {
    transform: (value: boolean | string) =>
    typeof value === 'string' ? value === '' : value,
    });
    }


    Finally, I want to extend this component to create concrete action components, like a create, edit, delete, etc button, where the only thing that changes is button color, and icon being shown:

    @Component({
    selector: '[createButton]',
    imports: [CommonModule],
    template: `
    <ng-container
    [ngTemplateOutlet]="buttonTpl"
    [ngTemplateOutletContext]="createBtnContext"
    />
    `,
    host: {
    '[class.btn-success]': 'true',
    },
    changeDetection: ChangeDetectionStrategy.OnPush,
    })
    export class CreateButtonComponent extends AbstractActionButtonComponent {
    protected readonly createBtnContext = {
    icon: 'fa-circle-plus',
    label: 'Create'
    };
    }


    This would get used in my project like:

    <button
    createButton
    [disabled]="createDisabled()"
    (click)="create.emit()"
    >
    Create
    </button>


    The problem I am facing is the TemplateRef defined inside AbstractActionButtonComponent is undefined at the time I want to reference it in the CreateButtonComponent.

    How can I ensure that this template is defined and available without having to explicitly define it in my other templates?

    Im trying to maintain a clean DOM structure.

    Continue reading...

Compartilhe esta Página