Handle Recursive Inner Child Elements in Angular
Tags: angular, recursivechild, ngtemplate, ngcontainer
Published: 2020-04-26 15:12:32

---
title: Handle Recursive Inner Child Elements in Angular
published: true
description: Let's see how we can manage recursive child elements in Angular using 2 methods
tags: angular, recursive-child, ng-template, ng-container
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/4krvbfzgyygmkmzgh2mq.png
---

*Many times, we face a situation, where we need some kind of architecture that helps us achieve recursive occurrence of child elements within same child elements. For example, replies or comments of in a discussion. Each reply has same functionality and UI and there can be many replies under one reply.*

---

## First things first

Open up your 👨‍💻 terminal and run

```bash
npm i -g @angular/cli
ng new recursive-child --defaults --minimal --inlineStyle
```
> *ℹ️ Tip: Do not use `--minimal` option in actual app. We are using it here only for learning purpose. You can learn more about CLI Options [here](https://angular.io/cli/new).*

```bash
cd recursive-child
ng serve -o
```

Great 👍. We have completed the initial setup. You’ve done a lot today. What a 🌄 day. You should take a 🛌 rest. Go 😴 nap or get a 🍲 snack. Continue once you're 😀 awake.

## Code

We will try to keep this as minimum as possible.

First, open *src\app\app.component.ts* and add a class property name `replies`:

```ts
// src\app\app.component.ts

...
export class AppComponent {
  replies = [
    {
      id: 1,
      value: 'Lorem'
    },
    {
      id: 2,
      value: 'Ipsum'
    },
    {
      id: 3,
      value: 'Dolor'
    },
    {
      id: 4,
      value: 'Sit'
    }
  ]
}
```

and also replace the *template* HTML and *styles* with below:

```ts
// src\app\app.component.ts

...
template: `
    <ul>
      <li *ngFor="let reply of replies"><b>{{reply.id}}:</b> {{reply.value}}</li>
    </ul>
  `,
  styles: [
    "ul { list-style: none }"
  ]
...
```

The output will look like below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/uxr8fmiwzoo4jyu2w102.png)

Now, ideally the property `replies` should be coming from your API and you should set it in `ngOnInit` life-cycle hook.

As we discussed initially, in actual scenarios, a `reply` can have many `replies`. So, let's make change for the in our property:

```ts
// src\app\app.component.ts

...
replies = [
    {
      id: 1,
      value: 'Lorem',
      children: [
        {
          id: 1.1,
          value: 'consectetur',
          children: [
            {
              id: '1.1.1',
              value: 'adipiscing '
            }
          ]
        }
      ]
    },
    {
      id: 2,
      value: 'Ipsum'
    },
    {
      id: 3,
      value: 'Dolor',
      children: [
        {
          id: 3.1,
          value: 'eiusmod'
        },
        {
          id: 3.2,
          value: 'labore',
          children: [
            {
              id: '3.2.1',
              value: 'aliqua'
            }
          ]
        }
      ]
    },
    {
      id: 4,
      value: 'Sit'
    }
  ]
```

Now, this won't change anything in the output. Because we haven't handled `children` in our `template`.

Let's try something. Change `template` HTML to below:

```ts
// src\app\app.component.ts

...
template: `
    <ul>
      <li *ngFor="let reply of replies">
        <b>{{ reply.id }}:</b> {{ reply.value }}
        <ul *ngIf="reply.children">
          <li *ngFor="let childReply of reply.children">
            <b>{{ childReply.id }}:</b> {{ childReply.value }}
          </li>
        </ul>
      </li>
    </ul>
  `,
```

So, what we are doing above:

1. We are looping through all `replies`
2. We are printing each `reply`'s `id` and `value` in `<li>`
3. Next, in `<li>` we are checking if that reply has children
4. If so, we are creating child list and showing the `id` and `value`

The output looks like below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ymawlzbmfodgkxq9fsxm.png)

It worked, right? Yes, but... it's showing just first level of children. With our current approach, we can't cover all levels of children in each reply. Here, we need some 🤯 dynamic solution. There can be 2 ways to achieve this.

## 1. `ng-template` & `ng-container`

First, let's see what `ng-template` is, from Angular's [documentation](https://angular.io/guide/structural-directives#the-ng-template):

> *The <ng-template> is an Angular element for rendering HTML. It is never displayed directly. In fact, before rendering the view, Angular replaces the <ng-template> and its contents with a comment.*

Simply put, `ng-template` does not render anything *directly* whatever we write inside it. I wrote *directly*, so it must render *indirectly*, right?

We can render content of `ng-template` using `NgTemplateOutlet` directive in `ng-container`.

> *The Angular `<ng-container>` is a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM.*

Angular doesn't render `ng-container`, but it renders content inside it.

> *`NgTemplateOutlet` Inserts an embedded view from a prepared TemplateRef.*

`NgTemplateOutlet` takes an expression as input, which should return a `TemplateRef`. `TemplateRef` is nothing but `#template` given in `ng-template`. For example, `templateName` is `TemplateRef` in below line:

```html
<ng-template #templateName> some content </ng-template>
```

We can also give some data to `ng-template` by setting ` [ngTemplateOutletContext]`. `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding by the local template let declarations. Using the key `$implicit` in the context object will set its value as default.

See below code for example:

```ts
// example

@Component({
  selector: 'ng-template-outlet-example',
  template: `
    <ng-container *ngTemplateOutlet="eng; context: myContext"></ng-container>
    <ng-template #eng let-name><span>Hello {{name}}!</span></ng-template>
`
})
export class NgTemplateOutletExample {
  myContext = {$implicit: 'World'};
}
```

What's happening in above example:

1. We created a `<ng-template>` with `#eng` as *TemplateRef*. This template also prints the `name` from it's context object, thanks to `let-name`.
2. We created a `<ng-container>`. We asked it to render `eng` template with `myContext` as context.
3. We created `myContext` class property, which has only one key-value pair: `{$implicit: 'World'}`. Thanks to `$implicit`, it's value is set as default value in `<ng-template>`
4. `<ng-template>` uses `let-name`, accesses default value from `myContext` and assigns it in `name` and it prints

Okay. Let's see how we can use all of it in our problem.

Let's change the `template` HTML code to below:

```ts
// src\app\app.component.ts

...
template: `
    <ng-container
      *ngTemplateOutlet="replyThread; context: { $implicit: replies }"
    ></ng-container>
    <ng-template #replyThread let-childReplies>
      <ul>
        <li *ngFor="let reply of childReplies">
          <b>{{ reply.id }}:</b> {{ reply.value }}
          <ng-container *ngIf="reply.children">
            <ng-container
              *ngTemplateOutlet="
              replyThread;
                context: { $implicit: reply.children }
              "
            ></ng-container>
          </ng-container>
        </li>
      </ul>
    </ng-template>
  `,
...
```

Almost everything is same as what was happening in previous example, but there are few additional things which are happening here. Let's see in details:

1. We are creating a `<ng-container>`. And we are asking it to render `replyThread` template with `{ $implicit: replies }` as context.
2. Next, we are creating a `<ng-template>` with `replyThread` as *TemplateRef*. We are also using `let-childReplies`, so that inner code can use `childReplies`.
3. Now, in `<ng-template>`, first we are looping through all `childReplies`.
4. Then, we are checking, if any `reply` of `childReplies` has children.
5. If yes, then we are repeating step 1, but with `{ $implicit: reply.children }` as context.

Now, the output is like below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kz66r2ukdqq3h2x4lseb.png)

Cool, it renders all the levels of child replies. Now, let's look at the second approach.

## 2. A `reply` Component

Instead of using `ng-container` and `ng-template`, we can also create a component to achieve same behavior.

Let's create a component:

```bash
ng g c reply
```

It will create a folder and component inside it like below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lyaot02olck90voqzwwb.png)

Let's open *src\app\reply\reply.component.ts* and edit it like below:

```ts
// src\app\reply\reply.component.ts

import { Component, OnInit, Input } from "@angular/core";

@Component({
  selector: "app-reply",
  template: `
    <ul>
      <li *ngFor="let reply of replies">
        <b>{{ reply.id }}:</b> {{ reply.value }}
      </li>
    </ul>
  `,
  styles: [],
})
export class ReplyComponent implements OnInit {
  @Input() replies: { id: string | number; value: string; children: any[] }[];

  constructor() {}

  ngOnInit(): void {}
}
```

Here, we did 2 main things:

1. We are accepting `replies` as `@Input()`
2. We are looping through all the replies and printing `id` and `value` in `ul` > `li`

Let's use `app-reply` component in our main `app-root` component:

```ts
// src\app\app.component.ts

...
template: `
    <app-reply [replies]="replies"></app-reply>
  `,
...
```

Well, the output still reflects only 1st level of replies:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/uxr8fmiwzoo4jyu2w102.png)

Let's handle `children`, too:

```ts
// src\app\reply\reply.component.ts

...
template: `
    <ul>
      <li *ngFor="let reply of replies">
        <b>{{ reply.id }}:</b> {{ reply.value }}
        <!-- 🚨 Note the usage of component inside same component -->
        <app-reply *ngIf="reply.children" [replies]="reply.children"></app-reply>
      </li>
    </ul>
  `,
...
```

You noticed the change, right? We're using `<app-reply>` again inside `<app-reply>` if that `reply` has children.

Now the output is correct, it renders all levels of replies:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/kz66r2ukdqq3h2x4lseb.png)

The code is available at a public Github repo:

{% github shhdharmen/recursive-child %}

## Thank you,

For reading this article. Let me know your feedback and suggestions in comments sections.

And yes, always believe in yourslef:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/b56ionakfhvbs0vr0gyl.jpg)

#### Credits
<small>Footer: <span>Photo by <a href="https://unsplash.com/@catalinpop?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Cata</a> on <a href="/s/photos/believe?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></small>