Angular structural directive for sharing data as local variable into html component template
Nigro Simone
Posted on March 31, 2022
Sometime there is a need to share data into component template as local variable.
In this example timer$
is subscribed two time:
import { Component } from '@angular/core';
import { defer, Observable, timer } from 'rxjs';
@Component({
selector: 'app-root',
template: `
<div>
1: {{ timer$ | async }} <!-- first subscription -->
</div>
<div>
2: {{ timer$ | async}} <!-- second subscription -->
</div>
`,
})
export class AppComponent {
timer$: Observable<number> = defer(() => timer(3000, 1000));
}
The idea is create a structural directive ngLet
for sharing data as local variable into html component template, eg.:
import { Component } from '@angular/core';
import { defer, Observable, timer } from 'rxjs';
@Component({
selector: 'app-root',
template: `
<ng-container *ngLet="timer$ | async as time"> <!-- single subscription -->
<div>
1: {{ time }}
</div>
<div>
2: {{ time }}
</div>
</ng-container>
`,
})
export class AppComponent {
timer$: Observable<number> = defer(() => timer(3000, 1000));
}
This structural directive create local context of variable that can be used into html template:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
interface NgLetContext<T> {
ngLet: T;
$implicit: T;
}
@Directive({
selector: '[ngLet]'
})
export class NgLetDirective<T> {
private context: NgLetContext<T | null> = { ngLet: null, $implicit: null };
private hasView: boolean = false;
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<NgLetContext<T>>) { }
@Input()
set ngLet(value: T) {
this.context.$implicit = this.context.ngLet = value;
if (!this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef, this.context);
this.hasView = true;
}
}
/** @internal */
public static ngLetUseIfTypeGuard: void;
/**
* Assert the correct type of the expression bound to the `NgLet` input within the template.
*
* The presence of this static field is a signal to the Ivy template type check compiler that
* when the `NgLet` structural directive renders its template, the type of the expression bound
* to `NgLet` should be narrowed in some way. For `NgLet`, the binding expression itself is used to
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgLet`.
*/
static ngTemplateGuard_ngLet: 'binding';
/**
* Asserts the correct type of the context for the template that `NgLet` will render.
*
* The presence of this method is a signal to the Ivy template type-check compiler that the
* `NgLet` structural directive renders its template with a specific context type.
*/
static ngTemplateContextGuard<T>(dir: NgLetDirective<T>, ctx: any): ctx is NgLetContext<Exclude<T, false | 0 | '' | null | undefined>> {
return true;
}
}
With ngLet
you can create into html template every variable you want:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<ng-container *ngLet="(num1 + num2) as total"> <!-- single computation -->
<div>
1: {{ total }} <!-- 3 -->
</div>
<div>
2: {{ total }} <!-- 3 -->
</div>
</ng-container>
`,
})
export class AppComponent {
num1: number = 1;
num2: number = 2;
}
This trick is also published as npm library:
https://www.npmjs.com/package/ng-let
Online demo:
https://stackblitz.com/edit/demo-ng-let
Source code:
https://github.com/nigrosimone/ng-let
π πͺ π
π©
Nigro Simone
Posted on March 31, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
angular Incremental Hydration in Angular 19: Take Your Appβs Performance to the Next Level
November 2, 2024
angular Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΠΈΡΠΊΠ° ΠΈ ΠΈΠ½ΡΠ΅Π³ΡΠ°ΡΠΈΡ Ρ Π²Π½Π΅ΡΠ½ΠΈΠΌ API Π² Angular 18
September 13, 2024