Todd Motto

Todd Motto

Owner, Ultimate Angular

Angular ngFor, <ng-template> and the compiler
Feb 1, 2017
13 mins read
Edit post

Angular ngFor is a built-in Directive that allows us to iterate over a collection. This collection is typically an array, however can be “array-like”. To demonstrate this, we’ll be using Rx.Observable.of() to initialise our collection with an Observable instead of a static array.

We’ll also be exploring some other under-the-hood properties of ngFor, as well as looking at how Angular expands our ngFor to a <ng-template> element and composes our view.

Note: In Angular v4 <template> has been deprecated in favour of <ng-template> and will be removed in v5. In Angular v2.x releases <template> is still valid.

Using the ngFor directive

In this section, we’ll be taking a look at the featureset ngFor provides, as well as some use case examples.

NgModule import

First of all, to use ngFor, you need to import the CommonModule. However, if you’re using the BrowserModule in the root module, you won’t need to import it, as the BrowserModule exposes the CommonModule for us - so you only need to import the CommonModule when creating further modules with @NgModule.

Our @NgModule will look like this:

import { NgModule } from [email protected]/core';
import { BrowserModule } from [email protected]/platform-browser';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  bootstrap: [AppComponent]
})
export class AppModule {}

For this article, we’ll be including a further ContactCardComponent component in our @NgModule:

// ...
import { ContactCardComponent } from './contact-card.component';

@NgModule({
  declarations: [
    AppComponent,
    ContactCardComponent
  ],
  // ...
})
export class AppModule {}

Our ContactCardComponent simply looks like this, taking a single @Input of contact:

import { Component, Input } from [email protected]/core';

import { Contact } from './models/contact.interface';

@Component({
  selector: 'contact-card',
  template: `
    <div class="contact-card">
      <p>{{ contact.name }} ( {{ contact.age }} )</p>
      <p>{{ contact.email }}</p>
    </div>
  `
})
export class ContactCardComponent {

  @Input()
  contact: Contact;

}

So now we’re all setup, what’s next?

Iterating collections

Now that our ContactCardComponent is included in our module, we can setup our AppComponent to use this dataset:

@Component({...})
export class AppComponent implements OnInit {
  contacts: Observable<Contact[]>;
  ngOnInit() {
    this.contacts = Observable.of([
      {
        "id": 1,
        "name": "Laura",
        "email": "[email protected]",
        "age": 47
      },
      {
        "id": 2,
        "name": "Walter",
        "email": "[email protected]",
        "age": 37
      },
      {
        "id": 3,
        "name": "Walter",
        "email": "[email protected]",
        "age": 49
      },
      {
        "id": 4,
        "name": "Jesse",
        "email": "[email protected]",
        "age": 47
      },
      {
        "id": 5,
        "name": "Irene",
        "email": "[email protected]",
        "age": 33
      }
    ]);
  }
}

As mentioned in the introduction, I’m using Observable.of here from RxJS to give me an Observable stream from the results, this is a nice way to mimic an Observable response, such as when using the Angular Http module to return data from your API.

Using ngFor

Now we’re setup, we can look into our AppComponent template:

@Component({
  selector: 'app-root',
  template: `
    <div class="app">
      <ul>
        <li>
          <contact-card></contact-card>
        </li>
      </ul>
    </div>
  `
})

You can see I’m declaring <contact-card> inside of here, as we want to iterate our dataset and populate each contact via the @Input setup inside our ContactCardComponent.

One way we could do this is using ngFor on the component itself, however for simplicity we’ll use the unordered list. Let’s add ngFor:

<ul>
  <li *ngFor="let contact of contacts">
    <contact-card></contact-card>
  </li>
</ul>

There are a few things happening here, the first you’ll notice a * character at the beginning of the ngFor, we’ll come onto what this means in the next section when we look at the <ng-template> element. Secondly, we’re creating a context called contact, using a “for of” loop - just like in ES6.

The ngFor Directive will clone the <li> and the child nodes. In this case, the <contact-card> is a child node, and a card will be “stamped out” in the DOM for each particular item inside our contacts collection.

So, now we have contact available as an individual Object, we can pass the individual contact into the <contact-card>:

<ul>
  <li *ngFor="let contact of contacts">
    <contact-card [contact]="contact"></contact-card>
  </li>
</ul>

If you’re using a static array, or binding the result of an Observable to the template, you can leave the template as it currently is. However, we can optionally bind the Observable directly to the template, which means we’ll need the async pipe here to finish things off:

<ul>
  <li *ngFor="let contact of contacts | async">
    <contact-card [contact]="contact"></contact-card>
  </li>
</ul>

You can check a live output of what we’ve covered so far here:

Using trackBy for keys

If you’re coming from an AngularJS background, you’ll have likely seen “track by” when using an ng-repeat, and similarly in React land, using key on a collection item.

So what do these do? They associate the objects, or keys, with the particular DOM nodes, so should anything change or need to be re-rendered, the framework can do this much more efficiently. Angular’s ngFor defaults to using object identity checking for you, which is fast, but can be faster!

This is where trackBy comes into play, let’s add some more code then explain:

<ul>
  <li *ngFor="let contact of contacts | async; trackBy: trackById;">
    <contact-card [contact]="contact"></contact-card>
  </li>
</ul>

Here we’ve added trackBy, then given it a value of trackById. This is a function that we’ll add in the component class:

trackById(index, contact) {
  return contact.id;
}

All this function does is use a custom tracking solution for our collection. Instead of using object identity, we’re telling Angular here to use the unique id property that each contact object contains. Optionally, we can use the index (which is the index in the collection of each item, i.e. 0, 1, 2, 3, 4).

If your API returns unique data, then using that would be a preferable solution over index - as the index may be subject to change if you reorder your collection. Using a unique identifier allows Angular to locate that DOM node associated with the object much faster, and it will reuse the component in the DOM should it need to be updated - instead of destroying it and rebuilding it.

You can check a live output of what we’ve covered here:

Capturing “index” and “count”

The ngFor directive doesn’t just stop at iteration, it also provides us a few other niceties. Let’s explore index and count, two public properties exposed to us on each ngFor iteration.

Let’s create another variable called i, which we’ll assign the value of index to. Angular exposes these values under-the-hood for us, and when we look at the next section with the <ng-template> element, we can see how they are composed.

To log out the index, we can simply interpolate i:

<ul>
  <li *ngFor="let contact of contacts | async; let i = index;">
    Index: {{ i }}
    <contact-card [contact]="contact"></contact-card>
  </li>
</ul>

This will give us every index, starting from 0, for each item in our collection. Let’s also expose count:

<ul>
  <li *ngFor="let contact of contacts | async; let i = index; let c = count;">
    Index: {{ i }}
    Count: {{ c }}
    <contact-card [contact]="contact"></contact-card>
  </li>
</ul>

The count will return a live collection length, equivalent of contacts.length. These can optionally be bound and passed into each <contact-card>, for example you may wish to log out the total length of your collection somewhere, and also pass the index of the particular contact into a function @Output:

<ul>
  <li *ngFor="let contact of contacts | async; let i = index; let c = count;">
    <contact-card
      [contact]="contact"
      [collectionLength]="c"
      (update)="onUpdate($event, i)">
    </contact-card>
  </li>
</ul>

You can check a live output of what we’ve covered here:

Accessing first, last, odd, even

Four more properties exposed by ngFor (well, actually underneath it uses NgForRow, a class which generates each ngFor item internally). Let’s quickly look at the source code for this:

export class NgForRow {
  constructor(public $implicit: any, public index: number, public count: number) {}
  get first(): boolean { return this.index === 0; }
  get last(): boolean { return this.index === this.count - 1; }
  get even(): boolean { return this.index % 2 === 0; }
  get odd(): boolean { return !this.even; }
}

As I mentioned above, the NgForRow is what constructs our ngFor items, and you can see in the constructor we’ve already taken a look at index and count! The last things we need to look at are the getters, which we can explain from the source code above:

  • first: returns true for the first item in the collection, matches the index with zero

  • last: returns true for the last item in the collection, matches the index with the total count, minus one to shift the “count” down one to cater for zero-based indexes

  • even: returns true for even items (e.g. 2, 4) in the collection, uses % modulus operator to calculate based off index

  • odd: returns true for odd items (e.g. 1, 3), simply inverts this.even result

Using this, we can add conditionally apply things such as styling, or hook into the last property to know when the collection has finished rendering.

For this quick demo, we’ll use ngClass to add some styles to each <li> (note how we create more variables, just like index):

<ul>
  <li
    *ngFor="let contact of contacts | async; let o = odd; let e = even;"
    [ngClass]="{
      'odd-active': o,
      'even-active': e
    }">
    <contact-card
      [contact]="contact"
      (update)="onUpdate($event, index)">
    </contact-card>
  </li>
</ul>

And some styles:

@Component({
  selector: 'app-root',
  styles: [`
    .odd-active { background: purple; color: #fff; }
    .even-active { background: red; color: #fff; }
  `],
  template: `
    <div class="app">
      <ul>
        <li
          *ngFor="let contact of contacts | async; let o = odd; let e = even;"
          [ngClass]="{
            'odd-active': o,
            'even-active': e
          }">
          <contact-card
            [contact]="contact"
            (update)="onUpdate($event, index)">
          </contact-card>
        </li>
      </ul>
    </div>
  `
})

We won’t demonstrate first and last, as it’s fairly obvious from the above how we can hook those up!

You can check a live output of what we’ve covered here:

<ng-template> element

We mentioned earlier in this article that we’d look at understanding what the * meant in our templates. This also shares the same syntax as *ngIf, which you’ve likely also seen before.

So in this next section, we’ll take a deeper dive on ngFor, * and the <ng-template> element to explain in more detail what’s really happening here.

When using an asterisk (*) in our templates, we are informing Angular we’re using a structural directive, which is also sugar syntax (a nice short hand) for using the <ng-template> element.

<ng-template> and Web Components

So, what is the <ng-template> element? First, let’s take a step back. We’ll roll back to showing some AngularJS code here, perhaps you’ve done this before or done something similar in another framework/library:

<script id="myTemplate" type="text/ng-template">
  <div>
    My awesome template!
  </div>
</script>

This overrides the type on the <script> tag, which prevents the JavaScript engine from parsing the contents of the <script> tag. This allows us, or a framework such as AngularJS, to fetch the contents of the script tag and use it as some form of HTML template.

Web Components introduced a new spec a few years ago similar to this idea, called <template>:

<template id="myTemplate">
  <div>
    My awesome template!
  </div>
</template>

To grab our above template and instantiate it, we’d do this in plain JavaScript:

<div id="host"></div>
<script>
  let template = document.querySelector('#myTemplate');
  let clone = document.importNode(template.content, true);
  let host = document.querySelector('#host');
  host.appendChild(clone);
</script>

Note how we have id=host, which is our “host” Node for the template to be injected into.

You may have seen this term floating around Angular in a few ways, such as _nghost prefixes on Nodes (ng-host) or the host property in directives.

ngFor and <ng-template>

First off, <ng-template> is Angular’s own implementation of the <template> tag, allowing us to think about application design in web components and the ideas behind them. It also provides us with more power than the <template> element gives us by default, seamlessly fitting into the way Angular compiles our code.

So how does the above <template> explanation tell us more about ngFor and the *? The asterisk is shorthand syntax for using the <ng-template> element.

Let’s start from the basic ngFor example:

<ul>
  <li *ngFor="let contact of contacts | async">
    <contact-card [contact]="contact"></contact-card>
  </li>
</ul>

And demonstrate the <ng-template> equivalent:

<ul>
  <ng-template ngFor let-contact [ngForOf]="contacts | async">
    <li>
      <contact-card [contact]="contact"></contact-card>
    </li>
  </ng-template>
</ul>

That’s a lot different! What’s happening here?

When we use *ngFor, we’re telling Angular to essentially treat the element the * is bound to as a template.

Angular’s <ng-template> element is not a true Web Component (unlike <template>). It merely mirrors the concepts behind it to allow you to use <ng-template> as it’s intended in the spec. When we compile our code (JiT or AoT), we will see no <ng-template> elements outputted in the DOM. However, this doesn’t mean we can’t use things like Shadow DOM, as they are still completely possible.

Let’s continue, and understand what ngFor, let-contact and ngForOf are doing above.

ngFor and embedded view templates

First thing’s first, ngFor is a directive! Let’s check some of the source code:

@Directive({selector: '[ngFor][ngForOf]'})
export class NgFor implements DoCheck, OnChanges {...}

Here, Angular is using attribute selectors as the value of selector to tell the @Directive decorator what attributes to look for (don’t confuse this with property binding such as <input [value]="foo">).

The directive uses [ngFor][ngForOf], which implies there are two attributes as a chained selector. So, how does ngFor work if we’re not using ngForOf?

Angular’s compiler transforms any <ng-template> elements and directives used with a asterisk (*) into views that are separate from the root component view. This is so each view can be created multiple times.

During the compile phase, it will take let contact of contacts and capitalise the of, and create a custom key to create ngForOf.

In our case, Angular will construct a view that creates everything from the <li> tag inwards:

<!-- view -->
<li>
  <contact-card [contact]="contact"></contact-card>
</li>
<!-- /view -->

It also creates an invisible view container to contain all instances of the template, acting as a placeholder for content. The view container Angular has created essentially wraps the “views”, in our case this is just inside the <ul> tags. This houses all the templates that are created by ngFor (one for each row).

A pseudo-output might look like this:

<ul>
<!-- view container -->
  <!-- view -->
  <li>
    <contact-card [contact]="contact"></contact-card>
  </li>
  <!-- /view -->
  <!-- view -->
  <li>
    <contact-card [contact]="contact"></contact-card>
  </li>
  <!-- /view -->
  <!-- view -->
  <li>
    <contact-card [contact]="contact"></contact-card>
  </li>
  <!-- /view -->
<!-- /view container -->
</ul>

ngFor creates an “embedded view” for each row, passing through the view it has created and the context of the row (the index and the row data). This embedded view is then inserted into the view container. When the data changes, it tracks the items to see if they have moved. If they’ve moved, instead of recreating the embedded views, it moves them around to be in the correct position, or destroys them if they no longer exist.

Context and passing variables

The next step is understanding how Angular passes the context to each <ng-template>:

<ng-template ngFor let-contact [ngForOf]="contacts | async">
  <li>
    <contact-card [contact]="contact"></contact-card>
  </li>
</ng-template>

So now we’ve understood ngFor and ngForOf, how does Angular associate let-contact with the individual contact that we then property bind to?

Because let-contact has no value, it’s merely an attribute, this is where Angular provides an “implied” value, or $implicit as it’s called under-the-hood.

Whilst Angular is creating each ngFor item, it uses an NgForRow class alongside an EmbeddedViewRef, and passes these properties in dynamically. Here’s a small snippet from the source code:

changes.forEachIdentityChange((record: any) => {
  const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
  viewRef.context.$implicit = record.item;
});

Alongside this section of code, we can also see how our aforementioned index and count properties are kept updated:

for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
  const viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
  viewRef.context.index = i;
  viewRef.context.count = ilen;
}

You can dig through the directive source code in more detail here.

This is how we can then access the index and count like this:

<ul>
  <ng-template ngFor let-i="index" let-c="count" let-contact [ngForOf]="contacts | async">
    <li>
      <contact-card [contact]="contact"></contact-card>
    </li>
  </ng-template>
</ul>

Note how we’re supplying let-i and let-c values which are exposed from the NgForRow instance, unlike let-contact.

You can check out the <ng-template> version of the things we’ve covered here:

Jan 26, 2017

A deep dive on Angular decorators

Decorators are a core concept when developing with Angular (versions 2 and above). There’s also...

Feb 22, 2017

Getting started with Angular's Router

Angular’s router acts as the main hub of any application, it loads the relevant components...