Search code examples
javascriptangularngfor

Angular *ngFor over a map is running multiple times but should only run once


Stackblitz Demo of my app

In my app I try to loop over a map (with *ngFor) and display every map key in a new expansion panel and the values in the "body" of the expansion panel:

my app so far

The map has strings as keys and string arrays as values. After I have filled my map I pass it into the this.showMap. The only reason for this is that I can wait for showMap with *ngIf="showMap" in my HTML to make sure that all items are in the map before I show the Website to the user:

  showMap: any;

  ngOnInit() {
    let myMap: Map<string, string[]> = new Map<string, string[]>();
    myMap.set("food", ["apple", "sausage"]);
    myMap.set("money", ["bitcoin", "dollar"]);
    //... add more key, value pairs dynamically
    this.showMap = myMap;
  }

In my HTML I use an accordion and expansion-panels from material:

 <div *ngIf="showMap">

    <mat-accordion>
      <mat-expansion-panel hideToggle *ngFor="let key of getKeys(showMap); index as i">
        <mat-expansion-panel-header>
          <mat-panel-title>
            {{ key }}
          </mat-panel-title>
        </mat-expansion-panel-header>
        <p>{{ getValues(showMap)[i] }}</p>
      </mat-expansion-panel>
    </mat-accordion>
  </div>

and the getKeys() and getValues() function look like this and simply return the keys and values of the showMap:

getKeys(myMap: Map<string, string[]>) {
    let keys = Array.from(myMap.keys());
    console.log("keys: ", keys);
    return keys;
  }

  getValues(myMap: Map<string, string[]>) {
    let values = Array.from(myMap.values());
    console.log("values: ", values);
    return values;
  }

My app is running as wanted, but in the console logs I see that the functions getKeys() and getValues() are called multiple times. I'd expect that getKeys() gets called once (because of the *ngFor statement) and the getValues() gets called twice (once for each key). But it's way more, about 10 times?:

log


Solution

  • It is not consider a good practice call a function on the template, as mentioned by @Raz Ronen, function is executed every time Angular change detection runs. And that can be too many times! I would extract the values and keys into instance variables and use them instead of calling the function directly on the template.

    Check out this article about it: https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496