user647314
user647314

Reputation: 327

Angular how to global / application scope template

I need to reuse a parameterized menu template in many components to build up the local menu

<ng-template #menubutton let-link="link" let-icon="icon" let-text="text" ...>  
... does magic :)
</ng-template>

I tried to extract this part

  1. into an injectable common-menu component
  2. adding to app.component.html

but doesn't work and no any error.

pls put aside that this menu solution is very poor because the question can be generalized: how can we make application scope templates ?

Thanks in advance,

Csaba

Upvotes: 0

Views: 1382

Answers (1)

Hristo
Hristo

Reputation: 185

I know this is an old question but here's how I do it in my Angular project for years.

We need a simple map of TemplateRef instances:

import { TemplateRef } from "@angular/core";

export class TemplateStore {
    private templates: Map<string, TemplateRef<any>> = new Map<string, TemplateRef<any>>();

    public add(key: string, template: TemplateRef<any>): void {
        // The argument validation is omitted for brevity

        this.templates.set(key, template);
    }

    public get(key: string): TemplateRef<any> {
        return this.templates.get(key);
    }
}

And a directive that assigns a key to templates:

import { Directive, Input, TemplateRef } from "@angular/core";
import { TemplateStore } from "./template-store";

@Directive({
    selector: "[appTemplateKey]"
})
export class TemplateKey {
    constructor(
        private readonly template: TemplateRef<any>,
        private readonly templateStore: TemplateStore
    ) { }

    @Input("appTemplateKey")
    public set key(value: string) {
        this.templateStore.add(value, this.template);
    }
}

Then we put the global templates in the app component template:

<ng-template appTemplateKey="avatar" let-user>
    <a routerLink="/{{user.id}}" aria-hidden="true" tabindex="-1">
        <img [src]="user.pictureUrl" alt="" loading="lazy">
    </a>
</ng-template>

And the usage:

<ng-container *ngTemplateOutlet="templateStore.get('avatar'); context: {$implicit: currentUser}"></ng-container>

The inconvinience here is that we need to inject the TemplateStore into every component that uses it. Since in my Angular project all the components inherit a base class, we can avoid the injection by putting in the base class a method like this:

public template(key: string): TemplateRef<any> {
    return AppContext.current.templateStore.get(key);
}

The AppContext.current is a global object that holds a reference to the TemplateStore. So the usage become:

<ng-container *ngTemplateOutlet="template('avatar'); context: {$implicit: currentUser}"></ng-container>

Upvotes: 2

Related Questions