I am trying to avoid using method calls within Angular Templates as they are non-performant there. Lets say I have a list of names:
const names: string[] = ['Billy', 'Mandy', 'Carl', 'Sheryl']
and in my template im using an ngFor to iterate the list and print the names:
<ng-container *ngFor="let name of names">
<p>{{ name }}</p>
</ng-container>
But now I need to only display the name if it starts with an 'S' so I change to:
<ng-container *ngFor="let name of names">
<p *ngIf="doesNameStartWithS(name)">{{ name }}</p>
</ng-container>
Now I have a method in my template which I know is going to run more times than is necessary. To avoid this I could do something like:
// this runs whenever the names list changes
const nameStartsWithSList: boolean[] = this.names.map((name: string): boolean => this.doesNameStartWithS(name));
and then change my template to:
<ng-container *ngFor="let name of names; let i = index;">
<p *ngIf="nameStartsWithSList[i]">{{ name }}</p>
</ng-container>
but this has introduced a new list entirely to avoid the method call in the template. Is there a better way to go about doing this?
CodePudding user response:
That's a very interesting question.
One possible solution would be to pass the prefix and field to the directive and manipulate it accordingly. You could probably use the renderer2 as a better solution to present the paragraph with the field you want, but it was just to showcase it works.
@Input() chars: string;
@Input() field: string;
constructor(private el: ElementRef) {}
ngOnInit() {
if (this.field.toLowerCase().includes(this.chars.toLowerCase())) {
(this.el.nativeElement as HTMLElement).innerHTML = `<p>${this.field}</p>`;
}
}
Another thing (which I actually just realized) is that you can use the directive as a component too.
<ng-container *ngFor="let name of names">
<showIfStartsWith chars="s" [field]="name"></showIfStartsWith>
</ng-container>
Full demo here.
Edit: Found another solution less weird, without using the directive as a component. Demo V2
Edit 2: Found another solution, using the directive as a structural directive, showcasing how do you pass multiple parameters to it. Demo V3
CodePudding user response:
I think the best approach is to have your list manipulated in the ts file.
if your starting list is:
const names: string[] = ['Billy', 'Mandy', 'Carl', 'Sheryl']
set it up like
output(array, modifier) {
***modify array to return only starting with modifier letter***
return modifiedArray
}
modifier: string = 's'
const names: string[] = output(['Billy', 'Mandy', 'Carl', 'Sheryl'], modifier)
then use
<div *ngFor="let name of names"> ...