Angular Structural Directives, Part 2: NgFor
Tags: angular, ngconf, ngif
Published: 2021-03-01 14:38:57

Michi DeWitt | ng-conf | Oct 2020

_This is part of an ongoing series covering all the built-in Angular structural directives. This series is intended for new and experienced Angular developers. Previously we covered the [`ngIf` directive](https://dev.to/ngconf/angular-structural-directives-part-1-ngif-4fkf)._

---

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g8m8a7tfbc3qm5864gp5.png)
 

### **Introduction**
In Part 2 of our Angular Structural Directive series we’ll be covering the `ngFor` directive. Like the `ngIf` directive we covered previously, this directive is added as an attribute to any element in an HTML template.

### **What is the NgFor Directive and How is it Used?**
The `ngFor` directive is used to iterate over a collection of objects and add an instance of a specified template for each item in the collection.

Okay — that sounds very technical. What does that mean?

Here’s a simple example.

I’ve created a custom To Do list application for myself (because all demos must use To Do lists, right?). In my application, I have the following component that shows all my To Do list items:

```
<my-todo-list>
  <my-todo-item 
    title="Walk Dog" 
    description="Take the dog for a walk befor the gym">
  </my-todo-item>
  <my-todo-item 
    title="Gym" 
    description="Gym class at 5:30pm">
  </my-todo-item>
  <my-todo-item 
    title="Blog Post" 
    description="Write NgFor blog post">
  </my-todo-item>
</my-todo-list>
```

Here I have 3 items on my To Do list — “Walk Dog”, “Gym”, and “Blog Post”.

There are two problems with the list in this format.

First, this is a very static. If I want to add or remove something from my To Do list, I need to change the code and re-deploy my application.

Second and potentially more problematic, I can’t have any other people use my application because they would have no way to edit their own To Do list. This isn’t very flexible.

Instead, I can use the `ngFor` directive to iterate over a list of To Do items and add a new `my-todo-item` component for each item in my list.

Here’s the new code:

```
@Component({
    selector: 'my-ng-for-example',
    templateUrl: './my-ng-for-example.component.html',
})
export class MyNgForExample {
    public todoItems: TodoItem[] = [
        {
            title: 'Walk Dog',
            description: 'Take the dog for a walk before the Gym'
        },
        {
            title: 'Gym',
            description: 'Gym class at 5:30pm'
        },
        {
            title: 'Blog Post',
            description: 'Write ngFor Blog Post'
        }
    ]
}

export interface TodoItem {
    title: string;
    description: string;
}
```

```
<my-todo-list>
  <my-todo-item *ngFor="let item of todoItems" 
    [title]="item.title" 
    [description]="item.description">
  </my-todo-item>
</my-todo-list>
```

_Note: There is no example of the code in the `MyTodoItemComponent`, but you can assume it shows all the data a user needs to see in the To Do card and provides a UI for any actions on the card._

Much better! The refactored code that utilizes the `ngFor` directive has the following improvements:

1. DRY — I am not repeating the same block of code for each To Do item.
2. Dynamic — I can hook the todoItems array up to an Observable returned by a service or store so the list is always up to date and can be changed without re-deploying my code.
3. Extendible — If I want to add additional features, such as a button or completed state, I just have to update the one line in my template and all my To Do list items in the template will be updated.

### **Basic Usage**

Let’s take a step back and see how the `ngFor` directive works. The directive was added using the following snippet of code

```
*ngFor="let item of todoItems"
```

Let’s break that down.

1. The directive is added using the `*` syntax and the name of the directive — `ngFor`. The `*` let’s any developer know the attribute represents a structural directive
2. `todoItems` is a public property in my component that represents an array of data, in this case an array of `TodoItem` objects
3. The code `<my-todo-item *ngFor="let item of todoItems" ...></my-todo-item>` tells the compiler to add a new `my-todo-item` component for each `item` in the `todoItems` array. The directive will do this by iterating over each object in the `todoItems` array and keeps a reference to each object in a variable called `item`
4. Finally, I am able to customize each `MyTodoItemComponent` by referencing the `item` variable and binding data from the `item` to the `MyTodoItemComponent` inputs with `<my-todo-item *ngFor="let item of todoItems" [title]="item.title" [description]="item.description"></my-todo-item>`

Like the `ngIf` directive, the `ngFor` directive is easy to use because it maps so closely to the for-of Javascript syntax many Angular developers are already familiar with. You can mentally map the HTML syntax above to:

```
for (let item of todoItems) {
    // Execute this code for each item in todoItems
    console.log(`${item.title}: ${item.description}`);
}
```

### **Using Exported NgFor variables**

Sometimes you need to know additional information about each object’s position in the array to properly render each instance of the template.

Some common needs are:

* Add a different CSS class for every other element (example: different colors for rows in a table)
* Know the index of the object in the array when an action is executed (there will be an example for this later on)
* Know if the item is the first or last item in the array (example: add a margin to the bottom of the template if the item is not the last item in the array)

Luckily, Angular exports variables for all of these use cases!

* _Need to add a different CSS class for every other element? Use `even` or `odd`_
* _Need to know the of the index the object in the array when an action is executed? Use `index`_
* _Need to know if the item is the first or last item in the array? Use `first` or `last`, respectively_

Angular has [documentation](https://angular.io/api/common/NgForOf#local-variables) for all the variables that are available, but we’ll use `index` as an example in this post.

Let’s go back to our To Do list example. I want to add a new feature to delete an item from my To Do list (I don’t feel like going to the gym today). Let’s say the `MyTodoItem` component has a delete button that will emit an event whenever a user clicks on the button. But it’s the responsibility of the owner of the list to actually remove the To Do item from the list.

I’ve added a simple delete function, `deleteItem`, that will remove the To Do item at a specified index from my array of To Do items.

```
@Component({
    selector: 'my-ng-for-example',
    templateUrl: './my-ng-for-example.component.html',
})
export class MyNgForExample {
    public todoItems: TodoItem[] = [...];
    
    public deleteItem(index: number): void {
        todoItems.splice(index, 1);
    }
}
```

Now I’ll update the template to listen for delete output events from the `MyTodoItemComponent`.

```
<my-todo-list>
  <my-todo-item *ngFor="let item of todoItems; index as i" 
    [title]="item.title" 
    [description]="item.description"
    (delete)="deleteItem(i)">
  </my-todo-item>
</my-todo-list>
```

That’s all I need! Now each time the delete event is emitted for any To Do list item, the `deleteItem` function will be called with the index of item the delete output event fired from.

Let’s break down what code we added:

1. We stored the `index` variable that Angular provides using the `as` syntax in the `ngFor` directive with the code `index as i`. Now we can reference the index for each item with the `i` variable.
2. We added a listener on the `delete` output with `(delete)="deleteItem(i)"`. This calls the `deleteItem` function we added earlier and passes in the `i` variable we created from `ngFor` `index` local variable.

This example used the `index` variable specifically, but the same syntax can be used to store any of the other local variables, and there is no limit for how many variables may be added. That means you could do something like:

```
<my-todo-item *ngFor="let item of todoItems; index as i; even as 
isEven; first as isFirst">
   ...
</my-todo-item>
```

### **Tracking Changes**

The one bit of magic we haven’t covered yet is how changes are tracked. The `ngFor` directive will track changes in the array and will update the DOM whenever a change is detected for an object in the array. The following changes will trigger an update so the DOM properly reflects the contents of the array:

* A new object is added to the array
* A object is removed from the array (this is why the delete function we added above works)
* The items in the array are re-ordered

No additional code is required from the developer to make change detection work.

By default, the changes are tracked using the reference identity of each object in the array. The downside of tracking using the reference identity is that if you’re using a Reactive Programming strategy with immutable arrays and objects, you may be rebuilding all the objects in the array. This will cause the DOM to flash when there is a change to the array because it’s rebuilding **each** element created by the `ngFor` directive. Even if only one element was added or removed. Not only is this ugly, this can be very expensive!

As with other problems we’ve encountered, Angular provides a simple solution for this: add a custom `trackBy` function that is used to track changes. The `trackBy` function allows the developer to specify which value should be used to compare the objects if the object has changed. Generally, it’s best to return a unique id for the object that does not change when the object is rebuilt.

Let’s take a look at track by in action.

```
@Component({
    selector: 'my-ng-for-example',
    templateUrl: './my-ng-for-example.component.html',
})
export class MyNgForExample {
    public todoItems: TodoItem[] = [
        {
            id: '1',
            title: 'Walk Dog',
            description: 'Take the dog for a walk before the Gym'
        },
        {
            id: '2',
            title: 'Gym',
            description: 'Gym class at 5:30pm'
        },
        {
            id: '3',
            title: 'Blog Post',
            description: 'Write ngFor Blog Post'
        }
    ];
    
    public deleteItem(index: number): void { ... }
    
    public trackByFn(index: number, item: TodoItem): string {
        return item.id;
    }
}

export interface TodoItem {
    id: string;
    title: string;
    description: string;
}
```

In the code above, I’ve made the following changes:

* Updated the `TodoItem` interface to added a unique id for each To Do item
* Updated our array to add an id to each To Do item in the array
* Added a track by function that returns the id of the To Do item

Next, we’ll update the template:

```
<my-todo-list>
  <my-todo-item *ngFor="let item of todoItems; index as i; trackBy: trackByFn" 
    [title]="item.title" 
    [description]="item.description"
    (delete)="deleteItem(i)">
  </my-todo-item>
</my-todo-list>
```

Now, no matter how we update the array of `todoItems`, we’ll only rebuild the parts of the DOM that change rather than rebuilding the entire template.

Let’s take a step back and break down these changes we made.

1. We added the track by function to the `ngFor` directive with the following code `trackBy: trackByFn`. This tells the `ngFor` directive to use the function named `trackByFn` in my component to retrieve the value used to compare the changes to the array rather than the reference identity of the object.
2. We implemented the track by function in the component which takes in the `index` of the object in the array and the object itself. We return the value held in the id of the object. (In our case, we didn’t need to use the index).
3. Now when we add or remove an element to the `todoItems` array, instead of checking the reference identity of each item in the `todoItems` array, the directive will compare the id of the object before the change and the id after. If they are the same, it will rebuild the DOM item for that particular To Do item.

That’s all we need to do to use our own custom logic to track changes in the `ngFor` directive!

### **Summary**
We’ve now successfully used the `ngFor` directive to make a dynamic To Do list component that can be used by any user of our application.

Let’s go over the main points:

🛠 The `ngFor` directive is allows you to iterate over an array and add a new instance of a template for each item in the array.

ℹ️ Make use of the local variables Angular provides such as `index`, `even`, and `first` to get additional information about the position of each object in the array as you are iterating through.

🎯 Use the `trackBy` function to have better control over when the DOM rebuilds your template when you make changes to the array bound to your `ngFor` directive.

Now you are ready to use the `ngFor` directive in your next Angular application. Happy coding!

---

### **Additional Resources**
Angular `*ngFor` Documentation: [https://angular.io/api/common/NgForOf](https://angular.io/api/common/NgForOf)

## **ng-conf: The Musical is coming**
ng-conf: The Musical is a two-day conference from the ng-conf folks coming on April 22nd & 23rd, 2021. Check it out at [ng-conf.org](https://www.2021.ng-conf.org/)