Home > Software engineering >  Angular - How to iterate through an object and render children if they are present
Angular - How to iterate through an object and render children if they are present

Time:12-14

I have a Dto which has a child Dto of the same class like so:

export interface KeywordDto {
  children?: null | Array<KeywordDto>;
  id?: number;
  name?: null | string;
  parent?: KeywordDto;
}

And the nested limit in theory can be unlimited (it won't be), but the point is that we won't know how many times this will be nested. So I want to have a method that can take a KeywordDto, check for children then render that child and do the same for that Dto, and so on...

Here is my current attempt:

<mat-accordion multi>
    <mat-expansion-panel *ngFor="let keyword of keywords">
        <mat-expansion-panel-header>
            <mat-panel-title>
                {{ keyword.name }}
            </mat-panel-title>
        </mat-expansion-panel-header>
    <div [innerHTML]="renderKeywordChildren(keyword)"></div>
    </mat-expansion-panel>
</mat-accordion>
renderKeywordChildren(keyword: KeywordDto) {
  if (keyword.children?.length! > 0) {
    return `<mat-accordion multi>
      <mat-expansion-panel *ngFor="{{let keyword of keywords}}">
        <mat-expansion-panel-header>
            <mat-panel-title>
                {{ keyword.name }}
            </mat-panel-title>
        </mat-expansion-panel-header>
          
        <div [innerHTML]="renderKeywordChildren(keyword)"></div>
      </mat-expansion-panel>
    </mat-accordion>`;
  }

  return;
}

So the issue with this is that the returning string doesn't know about the Angular templating, it will just print out {{ keyword.name }}.

CodePudding user response:

Interesting question!

Your expected result will be nesting of the same template based on children.

Besides using <ng-template> with *ngIf will be simpler, the critical point is how to pass the value into the nested template and how the nested template uses the received value as variable.

<ng-container
  [ngTemplateOutlet]="keywordTemp"
  [ngTemplateOutletContext]="{ keywords: keywords }"
>
</ng-container>

<ng-template #keywordTemp let-keywords="keywords">
  <mat-accordion multi>
    <mat-expansion-panel *ngFor="let keyword of keywords">
      <mat-expansion-panel-header>
        <mat-panel-title>
          {{ keyword.name }}
        </mat-panel-title>
      </mat-expansion-panel-header>

      <ng-container
        *ngIf="(keyword.children?.length ?? 0) > 0"
        [ngTemplateOutlet]="keywordTemp"
        [ngTemplateOutletContext]="{ keywords: keyword.children }"
      >
      </ng-container>
    </mat-expansion-panel>
  </mat-accordion>
</ng-template>

Note: You may compare the previous version with the current version which improves in reducing code duplication.

Demo @ StackBlitz

  • Related