Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive template inside inner @for in Angular 17

I'm trying to create a recursive template an ran into a problem. My template:

<ng-template #recursiveGroups let-groups>
  @for (group of groups; track group; let i = $index) {
      <!-- Some stuff are shown -->
      @for (subGroup of group.subGroups; track subGroup;  let j = $index) {
        <ng-container *ngTemplateOutlet="recursiveGroups; context:{$implicit: data[i].subGroups[j]}"></ng-container>
        <!-- ALSO, SEPARATLY, TRIED: -->
        <ng-container *ngTemplateOutlet="recursiveGroups; context:{$implicit: subGroup}"></ng-container>
        <!-- OR: -->
        <ng-container *ngTemplateOutlet="recursiveGroups; context:{groups: subGroup}"></ng-container>
      }
  }
 </ng-template>
<ng-container *ngTemplateOutlet="recursiveGroups; context:{$implicit: data}"></ng-container>

My data looks like:

data: [
  {
    ...,
    children: [
      {
        ...
      }
    ],
    subGroups: [
      {
        ...,
        children: [
          {
            ...
          }
        ],
        subGroups: [
          {
            ...,
          }
        ],
      }
    ],
  },
  ...
]

Using context:{groups: <*>} shows only the top most level.

Using context:{@implicit: <*>} shows only the top most level and also raises error:

core.mjs:6531 ERROR TypeError: newCollection[Symbol.iterator] is not a function

What am I missing here? Thanks.

like image 874
Liran_T Avatar asked Sep 07 '25 19:09

Liran_T


2 Answers

Your recursiveGroups template expects an array, hence I don't see you need a nested @for for the subGroups. The template can be reused for the subGroups iteration.

Changes:

  1. Remove the nested @for.

  2. Provide the group.subGroups to the context for the nested <ng-container>.

<ng-template #recursiveGroups let-groups>
  @for (group of groups; track group; let i = $index) {
    
    <ng-container *ngTemplateOutlet="recursiveGroups; context:{ $implicit: group.subGroups }"></ng-container>
  }
</ng-template>

Demo @ StackBlitz

like image 110
Yong Shun Avatar answered Sep 11 '25 16:09

Yong Shun


You are running the forLoop at the top of the template, so there is no need for more for loops in recursion. All you need to do is to ensure the input for the recursive template remains as a array, then it will render correctly:

import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule],
  template: `
    <ng-template #recursiveGroups let-groups>
      @for (group of groups; track group; let i = $index) {
        {{group.id}}
        <hr/>
        <ng-container *ngTemplateOutlet="recursiveGroups; context:{$implicit: group.children}"></ng-container>
        <hr/>
        <ng-container *ngTemplateOutlet="recursiveGroups; context:{$implicit: group.subGroups}"></ng-container>
      }
    </ng-template>
    <ng-container *ngTemplateOutlet="recursiveGroups; context:{$implicit: data}"></ng-container>
  `,
})
export class App {
  name = 'Angular';
  data: any = [
    {
      id: 1,
      children: [
        {
          id: 2,
        },
      ],
      subGroups: [
        {
          id: 3,
          children: [
            {
              id: 4,
            },
          ],
          subGroups: [
            {
              id: 5,
            },
          ],
        },
      ],
    },
  ];
}

bootstrapApplication(App);

Stackblitz Demo

like image 33
Naren Murali Avatar answered Sep 11 '25 18:09

Naren Murali