HerexTheDog
HerexTheDog

Reputation: 123

More efficient way to call Angular method in template than the {{ }}?

I have the following bit of HTML in a template.

<button mat-icon-button [color]="color" *ngFor="let ratingId of ratingArr;index as i" [id]="'star_'+i" (click)="onClick(i+1)"
                [matTooltip]="'' + (ratingId + 1)"  matTooltipPosition="above">
    <mat-icon>{{showIcon(i)}}</mat-icon>
</button>

It uses this function inside these {{ }}

    showIcon(index: number): string {
        if (this.rating >= index + 1) {
            return 'star';
        } else {
            return 'star_border';
        }
    }

I was told there was a more efficient way to do this, but was given no details. Any insights?

Upvotes: 0

Views: 701

Answers (2)

ulmas
ulmas

Reputation: 2253

Calling functions in the template can potentially cause performance issues because the function may be called dozens or even hundreds of times per seconds, or lead to unexpected behavior (for example like this).

There are several ways around this.

1. Pure Pipes

Create a pipe that returns a value. You can then use the pipe like so:

 <mat-icon>{{ i | starsPipe }}</mat-icon>

2. OnPush ChangeDetectionStrategy

OnPush change detection strategy helps to reduce the potential problems caused by calling the function hundreds of times. However, if the values are passed to the component through inputs, this approach won't help.

3. Logic in the template

In your case, the simplest approach would be to use template logic like so:

<button mat-icon-button [color]="color" *ngFor="let ratingId of ratingArr;index as i" [id]="'star_'+i" (click)="onClick(i+1)"
                [matTooltip]="'' + (ratingId + 1)"  matTooltipPosition="above">
    <mat-icon>{{ rating >= i + 1 ? 'star' : 'star_border' }}</mat-icon>
</button>

Here, the same logic that you have in the method is used directly in the template.

Upvotes: 2

saivishnu tammineni
saivishnu tammineni

Reputation: 1242

The reason why this is approach is called inefficient is on every change detection cycle angular calls the showIcon method.

  1. This won't cause serious performance issues unless the method call is complex and time consuming (which isn't in your case). Something like iterating over a array of huge size (ex: 1000)
  2. An another way is to create an object with details needed to display the rating stars and use it in html template. So any method calls will be done only once during initialization
class ComponentClass {
public ratingIcons: string[] = [];

ngOnInit() {
   this.ratingIcons = this.ratingArr.map((rating, i) => showIcon(i));
}

HTML:

<button mat-icon-button [color]="color" *ngFor="let ratingIcon of ratingIcons;index as i" [id]="'star_'+i" (click)="onClick(i+1)">
    <mat-icon>{{ratingIcon}}</mat-icon>
</button>

As i don't know your complete application functionality here, so some changes may be required on top of this.The thing to understand here is how we create a new object that represents what we need exactly for the UI template and use it.

Upvotes: 1

Related Questions