Extending *ngFor in Angular to support “for…in”

Josep Sayol
4 min readDec 27, 2016

Disclaimer: the only point of this post is to give some insight into Angular’s structural directives. As a general rule you shouldn’t alter built-in directives like ngFor, since bad and unexpected things might happen to your app anytime.

You’ve probably used *ngFor to loop over the values of an Array. It tends to look like this:

<ul>
<li *ngFor=”let item of items”>{{ item }}</li>
</ul>

This snippet loops over the values of the iterable “items” and displays them in an unordered list. Pretty straightforward, but let’s find out what is Angular really doing under the hood to achieve this.

What are structural directives?

Keeping things simple, we can say there are two types of directives in Angular: attribute and structural. Attribute directives such as NgClass and NgStyle change the styling and behavior of an element, while structural directives like NgIf and NgFor alter the DOM tree by adding and removing DOM elements on the fly.

How does NgFor manage to do this?

Going back to our example, Angular transforms that little snippet into the following:

<ul>
<template ngFor [ngForOf]="items" let-item>
<li>{{ item }}</li>
</template>
</ul>

Whoa, hold on. What’s going on there? Angular takes our original *ngFor and breaks it down into its basic pieces: the variable or expression holding the values we want to iterate over, and the template variable that will hold each individual value so that we can use it.

The expression “let item of items” is what Angular calls a microsyntax, and in this case it gets split in two. “let item” becomes the let-item attribute while “of items” becomes the new property binding [ngForOf]=”items”. The “of” from “of items” simply gets appended to the original name of our directive, ngFor, to create the new ngForOf.

What would happen if we changed that “of” to something else? For example, if we write the following:

<ul>
<li *ngFor=”let item whatever items”>{{ item }}</li>
</ul>

Angular would translate it to this:

<ul>
<template ngFor [ngForWhatever]="items" let-item>
<li>{{ item }}</li>
</template>
</ul>

And then it would error out telling us it doesn’t know what to do with ngForWhatever.

for…in

NgFor is Angular’s equivalent of the for…of statement in JavaScript, looping over an iterable object and taking each of its values one by one. There’s also the for…in statement which instead iterates over the enumerable properties of an object, but there’s no Angular equivalent to that, so let’s make one.

First of all let’s define how this new directive will behave. If we have the following object in our model:

this.things = {
hello: 42,
world: true
};

And we apply the following template on it:

<div *ngFor="let name in things"> {{ name }} </div>

As we know by now this will transform into the actual template:

<template ngFor [ngForIn]="things" let-name>
<div> {{ name }} </div>
</template>

Which we want to eventually output this:

<div> hello </div>
<div> world </div>

We will start by defining the basic structure of our directive:

By using the @Directive() decorator we’re telling angular that the NgForIn class is a directive.

@Directive({
selector: '[ngFor][ngForIn]'
})

Its only argument is a selector. In our case we’re telling it to apply this directive when it finds the ngFor and ngForIn attributes. These are the ones that show up on the <template>, remember?

The class itself extends NgFor so that we can reuse all of its functionality, simply adding a few changes of our own. It’s also necessary to implement OnChanges, one of Angular’s many lifecycle hooks, so that we get notified whenever one of our input properties changes. Which leads us to:

@Input() ngForIn: any;

Here we are defining ngForIn as an input property of this directive, capturing the value of the ngForIn attribute into this property.

Almost done. The constructor simply takes all the necessary references via dependency injection and then just passes them along to NgFor. There’s no need right now to understand what exactly these do but if you’re interested you can take a look at the NgFor’s implementation.

Last but not least there’s the ngOnChanges method, the one that gets called by Angular whenever there’s changes to our input property. Its parameter is an object describing the changes that happened, containing the current value and the previous one. Inside this method is where we will implement our magic.

Since NgFor already does almost everything we need, all that remains is to take our object, get its enumerable properties, and pass them along. An initial implementation could look like this:

We’re taking three steps here:

  1. Get the object (the “things” in “let name in things”) that was passed into our directive via the input property ngForIn and extract an array of its keys. We assign that to the ngForOf property since that’s the one that NgFor expects as an input.
  2. Extract the currentValue and previousValue from the changes that happened to ngForIn and apply the same operation as before, creating a new “change” object for ngForOf.
  3. Invoke the original ngOnChanges method with these changes.

And that’s all! Including all the necessary imports, our directive should look like this:

There’s some improvements that can be made, in particular to better replicate the functionality of the for..in statement — for example by also supporting Map and other data structures besides plain objects. You can check out the gist with the final implementation over here.

Feel free to leave any comments below, or just get in touch with me to let me know what you think.

--

--

Josep Sayol

I‘m a software engineer. I recently spent 3+ years backpacking around the world, so there’s that