In this post we are going to go over the ngFor
core directive, namely we are going to go over the following:
- what
ngFor
does and what is its syntax - What are the most common errors associated to
ngFor
- Variable visibility
- Finding the index position of an element
- How to stripe a table using
even
andodd
- Identifying the
first
and thelast
element of a list - How does
ngFor
track items, why it can be important for performance? - How to use
trackBy
? - When to use
trackBy
? - Learn why
ngFor
is not only for arrays - A quick question for you at the end about learning
So let's get started diving into ngFor
! Below you can also find a video version of this post if you prefer, and the running code of this post is available here.
Note: If instead of ngIf you are looking for the @for
syntax, then check my other guide instead: Angular @for: Complete Guide.
The ngFor features are also covered in this video, have a look:
What can we do with ngFor
?
The core directive ngFor
allows us to build data presentation lists and tables in our HTML templates. Let's take for example the following data:
With ngFor
we can print this data to the screen under the form of a data table, by generating HTML similar to this:
What is the syntax of ngFor
?
To use ngFor
, let's create a component so that we can have a working HTML template:
This template will generate the HTML table that we showed just above. We can see in this example the (most common) syntax for using ngFor
:
- we are passing to
ngFor
an iteration expression - a loop variable named
hero
is defined using the keywordlet
, which is consistent with Javascript syntax - the expression is under the form of
var i of items
, which is consistent with the Javascriptof
iteration functionality
Variable Visibility
Note that the loop variable hero
is only visible inside the loop, you would not be able to access it outside the ngFor
section.
Common ngFor
Errors to watch out for
If you have an AngularJs background, you will see this error a few times before you get used to the new Angular syntax:
Can't bind to 'ngFor' since it isn't a known property of 'tr'
This is because you have accidentally either used item in items
instead of item of items
, or forgot to add the let
keyword at the beginning of the expression:
Finding the index of a list element
A very common requirement is to add to a list the numeric index position of its element. We can get the index of the current element by using the index
variable:
Note that you need the let keyword to get the value of the index, otherwise you will get an error similar to this one:
Parser Error: Unexpected token = at column ...
With this change, the HTML generated now looks like the following:
How to stripe a table using even
and odd
Another very common functionality needed when building tables is to be able to stripe a table by adding a different css class to the even or odd rows.
Let's say that to the above table we want to add a CSS class even
if the row is even and the CSS class odd
if the row is odd.
In order to so, we have a couple of variables available for that: even
and odd
, that can be used in the following way together with ngClass
:
Let's have a look at the HTML generated by this template:
As we can see, ngClass
added the CSS classes to the right rows, as we would expect.
Identifying the first
and the last
element of a list
Just like the even and odd functionality, there are also two other variables that can be used to identify the first and the last elements of the list:
This will add a CSS class named first
to the first element of the list, and a CSS class named last
to the last element of the list:
How does ngFor
work when we add or remove elements from the list?
As the input list gets modified, ngFor
will try to avoid to constantly create and destroy the DOM elements of the list, as this is an expensive operation. Also, when we pass to ngFor
a new list, this does not mean that the whole list will be re-built, meaning all the DOM re-created.
Many of the existing DOM elements will be reused and only some values inside them will be overwritten, and the decision is taken for each element in the list separately.
In order to take that decision Angular needs to identify each list element in a unique way, because for example if we pass in a new list with a different order, Angular will try to identify the elements and re-order the DOM elements of the list without deleting them and recreating them.
How are list items tracked by default?
ngFor
by default tracks list items using object identity. This means that if you build a list of new objects from scratch with the exact same values as the previous list and pass this newly built list to ngFor
, Angular will not be able to tell that a given list item is already present or not.
From a point of view of object identity, the new list contains a whole new set of items, completely different from the previous set. This is the case if for example we query the data again from the backend.
Tracking by object identity is a good default strategy because Angular has no information about the object so it cannot tell which property it should use for tracking.
Why can this be important for performance?
As we see, ngFor
already does a lot of optimizations out-of-the-box to try to reuse existing DOM elements as much as possible, but it's doing so based on object identity.
In the case of templates with large lists, or lists that occupy a large part of the screen, we might even with those optimizations still run into performance issues and notice that the UI is slow due to the large amount of DOM elements being created and destroyed.
If that happens to be the case, we can configure ngFor
to do the tracking by something else other than object identity.
How to use trackBy
?
We can provide our own mechanism for tracking items in a list by using trackBy
. We need to pass a function to trackBy
, and the function takes a couple of arguments, which are an index and the current item:
This implementation would do the tracking based on the id
property.
When to use trackBy
?
The use of trackBy
it's a performance optimization and is usually not needed by default, it's in principle only needed if running into performance issues.
Let's say that you are shipping to ancient mobile browsers or ancient versions of IE: you might want to consider applying trackBy
as a precaution in your larger tables, but it really depends on your system and the use case.
Is ngFor
only For Arrays?
In this example, we have been passing to ngFor
an array of Javascript objects, but actually we don't necessarily need to pass in an array to ngFor
in order for it to work.
We can pass to it any kind of Javascript Iterable in general, including Iterables that are created by the framework itself. To illustrate this, we are going to define a directive for a configuration element called hero
:
We can now use this configuration element in our template in the following way:
Now let's query this data from the configuration elements using @ContentChildren
:
Do you see what happened here? Turns out the QueryList
is a class that is part of Angular and is itself an Iterable! So although we can use it programmatically in the component class, we can also pass it directly to ngFor
and iterate over it directly.
And the same could be done with any other Iterable in our program.
I hope you enjoyed the post, I invite you to have a look at the list below for other similar posts and resources on Angular.
And if you would like to know about more advanced Angular Core features like ngFor, we recommend checking the Angular Core Deep Dive course, where we cover all the Angular core directives in great detail.
I invite you to subscribe to our newsletter to get notified when more posts like this come out:
If you are just getting started learning Angular, have a look at the Angular for Beginners Course:
Other posts on Angular
If you enjoyed this post, have also a look also at other popular posts that you might find interesting:
- Angular Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular Router - Extended Guided Tour, Avoid Common Pitfalls
- Angular Components - The Fundamentals
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular Forms - Template Driven vs Model Driven
- Angular ngFor - Learn all Features including trackBy, why is it not only for Arrays?
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
- How does Angular Change Detection Really Work?
- Typescript 2 Type Definitions Crash Course - Types and Npm, how are they linked? @types, Compiler Opt-In Types: When To Use Each and Why?