Home > Software engineering >  What are better ways to avoid using method calls within Angular Templates?
What are better ways to avoid using method calls within Angular Templates?

Time:12-10

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"> ...
  • Related