Logik
Logik

Reputation: 219

How to create a folder-structure with arbitrary depth in Angular?

I created an array of objects:

  folders = [
    {
      id: 1,
      folderName: "Folder1",
      files: ["File 1", "File 2"],
      folder: [
        {
          folderName: "Subfolder of folder1",
          folder: []
        }
      ]
    },
    {
      id: 2,
      folderName: "Folder2",
      files: ["File 1", "File 2"],
      folder: []
    }
  ]

and I want to display the information, also the information of the subfolder as a sublist under the folder name:

<ul *ngFor="let folder of folders">
  <li> {{folder.id}} {{folder.folderName}} 
    <ul>
      <li>{{folder.files}}</li>
    </ul>
  </li>
  <ng-container *ngIf="!(folder.folder.length <= 0)">
  <ul *ngFor="let folder2 of folder.folder">
    <li>
      {{folder2.id}} {{folder2.folderName}}
      <ul>
        <li>{{folder.files}}</li>
      </ul>
    </li>
  </ul>
</ng-container>
</ul>

Now I wonder how I could display a folder with an arbitrary number of subfolders. In my approach I always have to add code if I want to go one more level down but that would be repetitive. Is there another way of solving this problem?

Upvotes: 0

Views: 1499

Answers (3)

Felix Olszewski
Felix Olszewski

Reputation: 798

One solution for Indentation:

Parent:

<ng-container *ngFor="let folder of folders">
    <app-folder [folder]="folder" [indentationLevel]="0"></app-folder>
</ng-container>

Child:

<div class="folder-row" style="margin-left: {{indentationLevel * 32}}px" (click)="folder.isExpanded = !folder.isExpanded">
  <ion-icon class="folder-row__icon" *ngIf="folder.type === folderType.group" [name]="folder.isExpanded ? 'folder-open' : 'folder'"></ion-icon>
  <img class="folder-row__user-image" src="assets/mock-data/{{folder.userId}}.jpg" *ngIf="folder.type === folderType.user" alt="👤">
  {{folder.title}}
</div>
<ng-container *ngIf="folder.isExpanded">
  <ng-container *ngFor="let folder of folder.subFolders">
    <app-folder [folder]="folder" [indentationLevel]="indentationLevel + 1"></app-folder>
  </ng-container>
</ng-container>

How it works:

style="margin-left: {{indentationLevel * 32}}px"

this creates inline styling based on class value


parent: base case:

 [indentationLevel]="0"

folder: progress:

 [indentationLevel]="indentationLevel + 1"

Upvotes: 0

spots
spots

Reputation: 2708

I like to use ng-template for these kinds of things. You can define a re-usable template that in turn renders its self.

<ng-container *ngFor="let folder of folders">
    <ul>
        <ng-container [ngTemplateOutlet]="folderNode" [ngTemplateOutletContext]="{$implicit:folder}"></ng-container>
    </ul>
</ng-container>

<ng-template #folderNode let-folder>
    <li> {{folder.folderName}} </li>
    <li *ngFor="let file of folder.files">{{file}}</li>
    <ng-container *ngIf="folder.folder">
        <ng-container *ngFor="let subFolder of folder.folder">
            <ul>
                <ng-container [ngTemplateOutlet]="folderNode" [ngTemplateOutletContext]="{$implicit:subFolder}">
                </ng-container>
            </ul>
        </ng-container>
    </ng-container>
</ng-template>

Here is a working example.

Upvotes: 1

Ostn
Ostn

Reputation: 813

You can use a recursive component.

First you can define a Folder interface :

interface Folder {
  id: number;
  folderName: string;
  files: string[];
  folders: Folder[];
}

Then use it to define your data properly :

const folder: Folder = {
      id: 0,
      folderName: "Folder0",
      files: ["File 1", "File 2"],
      folders: [
        {
          id: 1,
          folderName: "Folder1",
          files: ["File 1", "File 2"],
          folders: [
            {
              id:3,
              files: ["File 1"],
              folderName: "Subfolder of folder1",
              folders: []
            }
          ]
        },
        {
          id: 2,
          folderName: "Folder2",
          files: ["File 1", "File 2"],
          folders: []
        }
      ]
    };

Then create a component to display any Folder object :

*.html

<h1>{{ folder.folderName }}</h1>
<ul>    
  <li *ngFor="let fileName of folder.files"> 
    {{ fildeName }} 
  </li>
</ul>
<!-- Display subfolders -->
<display-folder *ngFor="let subFolder of folder.folders" [folder]="subFolder"></display-folder>

*.ts

@Component({
    selector: 'display-folder',
    ...
})
export class DisplayFolder {
    @Input() folder: Folder;    
}

Upvotes: 2

Related Questions