Wanna see something cool? Check out Angular Spotify 🎧

Angular render recursive view using *ngFor and ng-template

We’re all familiar with the recursive view. One of the most common components that needed this technique is the nested navigation bar. The HTML structure and UI look like this.

<ul>
  <li><a href="#">Section 1</a></li>
  <li>
    <a href="#">Section 2</a>
    <ul>
      <li><a href="#">Section 2.1</a></li>
      <li><a href="#">Section 2.2</a></li>
      <li><a href="#">Section 2.3</a></li>
    </ul>
  </li>
  <li>
    <a href="#">Section 3</a>
    <ul>
      <li><a href="#">Section 3.1</a></li>
      <li>
        <a href="#">Section 3.2</a>
        <ul>
          <li><a href="#">Section 3.2.1</a></li>
          <li><a href="#">Section 3.2.2</a></li>
          <li>
            <a href="#">Section 3.2.3</a>
            <ul>
              <li><a href="#">Section 3.2.3.1</a></li>
              <li><a href="#">Section 3.2.3.2</a></li>
            </ul>
          </li>
        </ul>
      </li>
      <li><a href="#">Section 3.3</a></li>
    </ul>
  </li>
</ul>

But how to make one with Angular?

Solution

Imagine I have a class NavigationModel that represents the data above.

class NavigationModel {
  public title: string;
  public url?: string;
  public children: NavigationModel[];
}

So the trick is straightforward, you set up a ng-template which takes a list of NavigationModel as the parameter. Then render the template itself with children data if there are any children. Because children are also a list of NavigationModel. If you are not familiar with ngTemplateOutletContext, see this question for more detail

app.component.html

<ul>
  <ng-container
    *ngTemplateOutlet="recursiveListTmpl; context:{ list: list }"
  ></ng-container>
</ul>

<ng-template #recursiveListTmpl let-list="list">
  <li *ngFor="let item of list">
    {{ item.title }}
    <ul *ngIf="item.children.length > 0">
      <ng-container
        *ngTemplateOutlet="recursiveListTmpl; context:{ list: item.children }"
      ></ng-container>
    </ul>
  </li>
</ng-template>

app.component.ts

import { Component } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"]
})
export class AppComponent {
  public list: NavigationModel[] = [
    {
      title: "Section 1",
      children: []
    },
    {
      title: "Section 2",
      children: [
        {
          title: "Section 2.1",
          children: []
        },
        {
          title: "Section 2.2",
          children: []
        },
        {
          title: "Section 2.3",
          children: []
        }
      ]
    },
    {
      title: "Section 3",
      children: [
        { title: "Section 3.1", children: [] },
        {
          title: "Section 3.2",
          children: [
            {
              title: "Section 3.2.1",
              children: []
            },
            {
              title: "Section 3.2.2",
              children: []
            },
            {
              title: "Section 3.2.3",
              children: [
                {
                  title: "Section 3.2.3.1",
                  children: []
                },
                {
                  title: "Section 3.2.3.2",
                  children: []
                }
              ]
            }
          ]
        },
        {
          title: "Section 3.3",
          children: [
            {
              title: "Section 3.3.1",
              children: []
            },
            {
              title: "Section 3.3.2",
              children: []
            }
          ]
        }
      ]
    }
  ];
}

Then you have it. Another solution is to create a new component/ directive that takes input of NavigationModel[]. And do exactly the same thing as the ng-template example above.

Working Example


Published 29 Mar 2019

Read more

 — Skiing in Singapore - a coding diversion
 — npm - Check and update package if needed
 — CSS Layout - Horizontal & Vertical Align
 — How we handle time zone and locale at Zyllem
 — The myth of the Genius Programmer