Reputation: 1927
I'm trying to do something which should be simple: I would like to execute a function in an *ngFor. The function returns an object. I would like to set the object in a kind of "let" statement, so I can use its property in the HTML:
<div *ngFor="let productGroup of getproductGroupBySomeVariable(variable)">
<!-- This is where I need to set a variable with the output of
the getProductBySomeProperty function-->
<div *ngLet="'{{getProductBySomeProperty(productGroup.someproperty)}}'
as myVar" class="ui-g">
<!-- Here I want to use and display the properties of the
object created by the function above-->
<span>{{myVar.property1}} </span>
<span>{{myVar.property2}} </span> etc....
</div>
</div>
Upvotes: 5
Views: 10059
Reputation: 4897
I'm the author of https://www.npmjs.com/package/ng-let
source code of the directive: https://github.com/nigrosimone/ng-let/blob/main/projects/ng-let/src/lib/ng-let.directive.ts
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<T> {
return true;
}
}
Usage:
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));
}
Upvotes: 3
Reputation: 1387
Just wanted to mention an alternative solution that works really well:
<ng-container *ngIf="{ user: user | async } as context"> <!-- always true -->
{{ context.user }}
</ng-container>
Upvotes: 7
Reputation: 3926
You can create a directive for this, and use it this way:
Directive
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
interface ILetContext<T> {
ngLet: T;
}
@Directive({
selector: '[ngLet]'
})
export class LetDirective<T> {
private context: ILetContext<T> = { ngLet: undefined };
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<ILetContext<T>>) {
viewContainer.createEmbeddedView(templateRef, this.context);
}
@Input()
private set ngLet(value: T) {
this.context.ngLet = value;
}
}
Html
<ng-container *ngLet="getproductGroupBySomeVariable(variable) as productGroup;"></ng-container>
Upvotes: 7