angularngforangular-templatearray-indexing

Angular Index of nested loop inside Template


I have a nested loop in a table, but i want to only show a limited amount of rows in the DOM and load more when a button is clicked.

The Loop looks something like this:

<ng-container *ngFor="let organisation of organisations; let organisationIndex = index;">
    <ng-container *ngFor="let department of organisation.departments; let departmentIndex = index;">
        <ng-container *ngFor="let user of department.users; let userIndex = index;">
            <div>
                This is a List entry and i only want this 20 times in total (not for each loop)
            </div>
        </ng-container>
    </ng-container>
</ng-container>

What would be the smoothest way to get the index inside the nested loop, if possible without adding complexity?

I do not want to do it with css :nth-of-type or something similiar since my dataset is huge and the dom gets slow even if elements are hidden with css.

The length of each arrays is dynamic which prevents me from making a static formula (like organisationIndex * 50).


Solution

  • There are multiple options how you can deal with this one:

    Server pre-processing Trim the data on the server and send to client only set you would like to render. The advantage is the client's browser does not have to deal with large amount of data (and download them). Or introduce a "totalIndex" on each user that is incremental and continuous over organisations.

    Angular trimming - equal number of items in sub-collections

    If your items in your sub-collections contain the same number of items. You have to multiply the indexes and check whether you already output 20 rows. If so use *ngIf to avoid showing more of them. When using *ngIf item is not rendered at all.

    <ng-container *ngFor="let organisation of organisations; let organisationIndex = index;">
        <ng-container *ngFor="let department of organisation.departments; let departmentIndex = index;">
            <ng-container *ngFor="let user of department.users; let userIndex = index;">
                <div *ngIf="(organisationIndex * organisation.departments.length) + (departmentIndex * department.users.length) + userIndex < 20">
                    This is a List entry and i only want this 20 times
                </div>
            </ng-container>
        </ng-container>
    </ng-container>
    

    Angular trimming - counterpipe If number of items in sub-collections is not equal use counterpipe.

    import { Pipe, PipeTransform } from '@angular/core';
    import { Counter } from './counter';
    
    const counters = new WeakMap<any, Counter>();
    
    @Pipe({
      name: 'counterPipe'
    })
    export class CounterPipe implements PipeTransform  {
      transform(value: any): Counter {
        if (!counters.has(value)) {
          counters.set(value, new Counter());
        }
        return counters.get(value);
      }
    }
    

    This will add counter while processing entities in *ngFor so you always know the number of element you are rendering.

    Wrap your code with this pipe:

    <ng-container *ngFor="let counter of [organisations | counterPipe]">
      <ng-container *ngFor="let organisation of organisations; let organisationIndex = index;">
          <ng-container *ngFor="let department of organisation.departments; let departmentIndex = index;">
              <ng-container *ngFor="let user of department.users; let userIndex = index;">
                  <div *ngIf="counter.inc() < 20">
                    This is a List entry and i only want this 20 times
                  </div>
              </ng-container>
          </ng-container>
      </ng-container>
    </ng-container>
    

    Example of usage can be seen on https://stackblitz.com/edit/angular-nested-ngfor-ydvxjv