Did you know that you can directly call a function inside of Angular’s HTML templates?

For example, let’s say you have a home component class that has a list of restaurants, and each restaurant has some reviews:

import { Component } from '@angular/core';

export class HomePageComponent {
  restaurants = [
    {
      name: 'Los Tacos Locos',
      reviews: [
        { user: 'user1', review: 3 },
        { user: 'user2', review: 4 },
        { user: 'user7', review: 5 },
        { user: 'user3', review: 2 },
        { user: 'user4', review: 5 },
        { user: 'user5', review: 4 },
      ],
    },
    {
      name: "Jorge's BBQ",
      reviews: [
        { user: 'user1', review: 3 },
        { user: 'user2', review: 4 },
        { user: 'user7', review: 5 },
        { user: 'user3', review: 4 },
        { user: 'user4', review: 5 },
        { user: 'user5', review: 4 },
      ],
    },
  ];
}

And to display them, you have a regular list in your template, something like this:

<div class="restaurant-container">
  <div class="restaurant" *ngFor="let restaurant of restaurants">{{ restaurant.name }}</div>
</div>

What if we want to show also the average score of a restaurant? To display something like “Jorge’s BBQ 4,1 ⭐️”

For that, we’d need to calculate the average score, depending on your database provider, the best idea might be to calculate it on save, when you add a new review, calculate the new total, and then you have an easy to use variable to bind it to your templates.

But if this isn’t possible for whatever reason, you can still calculate it on the fly to render it.

First, let’s create a function that calculates this in our class.

import { Component } from '@angular/core';

export class HomePageComponent {
  restaurants = [
    {
      name: 'Los Tacos Locos',
      reviews: [
        { user: 'user1', review: 3 },
        { user: 'user2', review: 4 },
        { user: 'user7', review: 5 },
        { user: 'user3', review: 2 },
        { user: 'user4', review: 5 },
        { user: 'user5', review: 4 },
      ],
    },
    {
      name: "Jorge's BBQ",
      reviews: [
        { user: 'user1', review: 3 },
        { user: 'user2', review: 4 },
        { user: 'user7', review: 5 },
        { user: 'user3', review: 4 },
        { user: 'user4', review: 5 },
        { user: 'user5', review: 4 },
      ],
    },
  ];

  calculateRestaurantScore(restaurant): number {
    let averageScore = 0;
    restaurant.reviews.forEach(review => {
      averageScore = averageScore + review.review;
    });
    return averageScore / restaurant.reviews.length;
  }
}

And then, inside of your template, you can call it like this:

<div class="restaurant-container">
  <div class="restaurant" *ngFor="let restaurant of restaurants">
    The restaurant {{ restaurant.name }} has {{ calculateRestaurantScore(restaurant)}} ⭐️
  </div>
</div>

Since the output of that method is a number, you can even use pipes on it, for example, the current average score would output something like 3,8765456789, so we’ll use the number pipe to truncate it to 2 decimal places:

<div class="restaurant-container">
  <div class="restaurant" *ngFor="let restaurant of restaurants">
    The restaurant {{ restaurant.name }} has {{ calculateRestaurantScore(restaurant) | number: '1.0-2'}} ⭐️
  </div>
</div>

And that’s it.

The catch is that every time you call the function Angular’s change detection runs, so again, as I mentioned earlier, it’s always better to re-think or re-architecture your data to avoid this.

As usual, if you can offload the calculation work to the backend (or doing on write time so that the value is easily gotten from the DB) much better, but for things like these it works fine.

By the way, do you want to jump-start your development using the Firebase modular SDK with AngularFire?

Download my free book that will take you from creating an app to properly using Firebase Auth and Firestore.

Download the book now