Red Green Repeat Adventures of a Spec Driven Junkie

Learning Angular - Directives: ngFor

I am continuing my Learning Angular journey and understanding Angular’s ngFor directive.

I will extend my current example and show more images when typing keywords into a text field.

You will understand how ngFor works and how it can reduce code duplication, especially in the template.

This article will take you less than four minutes to read.

Nicolas Poussin - Blind Orion Searching For Rising Sun source and more information

Introduction

We can implement showing/hiding items based on a boolean value using just ngIf, a possible implementation would be:

<p *ngIf="show_ngIf">
  Type 'ngIf' to Display
</p>

<img *ngIf="show_dog"
  src="https://www.dropbox.com/s/ugeqlxvawfqwhth/dog_by_roaming_angel.jpeg?raw=1">

For two items, this implementation is fine. When there are more than three items, say: ngIf, dog, cat, cow, the above implementation starts to look like:

<p *ngIf="show_ngIf">
  Type 'ngIf' to display
</p>

<img *ngIf="show_dog"
  src="https://www.dropbox.com/s/ugeqlxvawfqwhth/dog_by_roaming_angel.jpeg?raw=1">

<img *ngIf="show_cat"
  src="https://www.dropbox.com/s/ip7trvghw45n4yc/cat.jpeg?raw=1">

<img *ngIf="show_cow"
  src="https://www.dropbox.com/s/h7kfnipb6ix1xxb/cow.jpeg?dl=0">

Displaying four items requires thirteen lines of code in the template. If there are more, this approach becomes unwieldily.

Following Along

If you would like to follow along with this code, you can use the Github link to get a final copy of the code or view the project in your browser using this StackBlitz link.

ngFor to the Rescue

Angular has another structural directive: ngFor, that is equivalent to a for loop in other programming languages.

Restructure data

Before we start, instead of having: show_<item> variable in our component code, I will put everything into a better data structure for the data and its intended usage.

    words = {
        "cat": {
            "show": false,
            "url": "https://www.dropbox.com/s/ip7trvghw45n4yc/cat.jpeg?raw=1",
        },
        "dog": {
            "show": false,
            "url": "https://www.dropbox.com/s/ugeqlxvawfqwhth/dog_by_roaming_angel.jpeg?raw=1",
        },
        "cow": {
            "show": false,
            "url": "https://www.dropbox.com/s/h7kfnipb6ix1xxb/cow.jpeg?raw=1",
        },
        "ngIf": {
            "show": false,
            "url": "",
        }
    }

Moving forward, words or this.words refer to the above data structure.

Listing Words

Before attacking the problem directly, lets use just ngFor to solve a problem.

Listing out the words available for the text box will make this interaction more user-friendly.

<ul *ngFor="let word of word_list" >
  <li>{\{ word }\}</li>
</ul>

The component code:

    word_list = Object.keys(this.words)

This displays in the browser:

  • cat
  • dog
  • cow
  • ngIf

Using ngFor with this new data structure, it’s easy to display all the available options. When adding more items to the data structure, there’s nothing to change in other parts of the code.

Syntax of ngFor

To iterate every item in an item_list array using ngFor would have syntax:

<div ngFor="let item in item_list">
	{\{ item }\}
</div>

Here, ngFor returns every element in item_list as item. Listing out words, I used: word_list to store all the words from the words data structure.

ngFor Limitation

JavaScript/Typescript has another function to get all the keys of an object: Object.keys(words). Why not use that?

ngFor in the template does not allow complex expressions, such as:

<div ngFor="let item in Object.keys(items)">
	{\{ item }\}
</div>

Where items is an object containing items. The way I solved this is to have item_list in the component that stores the items:

item_list = Object.keys(this.items)

Displaying Images with ngFor and ngIf

As we can easily list all the words out supported, how can we display the images associated with them?

The image tag needs to use ngIf to show when words.dog.show is true. For example:

  <img *ngIf="words.dog.show"
       [src]="words.dog.url">

Repeating this each time would just be the same as the initial implementation.

   <img *ngIf="words.ngIf.show"
        [src]="words.ngIf.url">
   <img *ngIf="words.dog.show"
        [src]="words.dog.url">
   <img *ngIf="words.cat.show"
        [src]="words.cat.url">
   <img *ngIf="words.cow.show"
        [src]="words.cow.url">

It’s down from thirteen lines to eight lines, so there is an improvement.

Can we do better?

With ngFor, there is a better way!

Using the word_list and ngFor, to display all the images where the words[word].show is true, the code can be:

<div *ngFor="let word of word_list" >
  <img *ngIf="words[word].show"
       [src]="words[word].url">
</div>

In the twice the number of lines as showing one image, we can display all the proper images, even when adding more entries to words list.

Conclusion

Using ngFor with the right data structure allows for efficient processing of lists.

ngFor syntax is similar to JavaScript/Type Script’s for syntax and available in the template for simple evaluation.