DEV Community

Cover image for ngFor Done Right
Michele Da Rin
Michele Da Rin

Posted on • Originally published at blog.daudr.me

ngFor Done Right

This article has been previously published on my main blog

When dealing with lists in templates, ngFor saves us a lot of time, but it could be a real pain if used wrong, read this article to know hot use it right.

Arrive first with ngFor

Photo by Kolleen Gladden on Unsplash

All the information on this article can also be found in the official docs

Prerequisites

During this guide we will use an interface that represents our lists' object, let's call it Item, that'll look something like this:

export interface Item {
  _id: string
  name: string
}

and we will use some utils mock and functions that'll help us later:

export const items: Item[] = [{ _id: "a2fHdS8P", name: "Mike" }]

export const makeRandomId = (): string => {
  let result = ""
  const characters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
  const charactersLength: number = characters.length
  for (let i = 0; i < 8; i++) {
    result += characters.charAt(Math.floor(Math.random() * charactersLength))
  }
  return result
}

export const makeRandomName = (): string => {
  const names = [
    "Sara",
    "Mattia",
    ...
  ]

  return names[Math.floor(Math.random() * names.length)]
}

export const itemsLong: Item[] = [
  {
    _id: makeRandomId(),
    name: makeRandomName(),
  },
  {
    _id: makeRandomId(),
    name: makeRandomName(),
  },
  ...
]

First of all trackBy

trackBy is a function that defines how to track changes for items in the iterable. That means that we can use it to know exactly which element in a list are being changed, after a network call or whatsoever.
We implement it by defining a function in our .ts file:

trackByFunction (index, item) {
  return item._id;
}

then in your .html add your new function

<div *ngFor="let item of items; trackBy: trackByFunction"></div>

Doing this lets your app be more performant as it doesn't need to reload all of your view but only the element that's being changed.

even and odd comes next

The variables even and odd are mainly used for styling the components.
For example, when we have a table and we want to make it more readable we can use these variables to change the background color of the rows.

In your html

<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <tr
      *ngFor="let item of items; trackBy: trackBy; odd as odd; even as even"
      [ngClass]="{ 'odd': odd, 'even': even }"
    >
      <td>{{ item._id }}</td>
      <td>{{ item.name }}</td>
    </tr>
  </tbody>
</table>

And in your css/scss file

.odd {
  background-color: rgba(255, 0, 255, 0.45);
}

.even {
  background-color: rgba(0, 0, 0, 0.45);
}

The result will be something like
Even and odd example

Tell me about first and last

Just like even and odd, first and last variables are used for styling purposes too.
Let's use the already created table and set the classes only for the first and last row:

<table>
  <thead>
    <tr>
      <th>ID</th>
      <th>Name</th>
    </tr>
  </thead>
  <tbody>
    <tr
      *ngFor="let item of items; trackBy: trackBy; first as first; last as last"
      [ngClass]="{ 'first': first, 'last': last }"
    >
      <td>{{ item._id }}</td>
      <td>{{ item.name }}</td>
    </tr>
  </tbody>
</table>
.first {
  background-color: rgba(0, 0, 255, 0.45);
}

.last {
  background-color: rgba(0, 255, 0, 0.45);
}

That'll look like this
First and last example

Don't forget the index

The index property does exactly what you think it does, it tells the index of the element in a list.
Maybe it's the property that's being used the most when we are dealing with lists, just think if you want to lay your lists' element in defined rows using css.

<mat-list>
  <mat-list-item *ngFor="let item of items; trackBy: trackByFunction; index as index">{{ item.name }} index is {{ index }}</mat-list-item>
</mat-list>

Index example

All the code used in this guide can be found on Stackblitz and GitHub

Top comments (0)