167

How do I use *ngFor to repeat a HTML element multiple times?

For eg: If I have a member variable assigned to 20. How do I use the *ngFor directive to make a div repeat 20 times?

takeradi
  • 3,661
  • 7
  • 29
  • 53
  • 1
    See also https://stackoverflow.com/questions/47586525/parse-array-in-angular-template/47586692#47586692 – yurzui Dec 01 '17 at 04:55
  • There are *four ways* to achieve the same, read out here. https://stackoverflow.com/a/36356629/5043867 – Pardeep Jain Aug 22 '19 at 11:58
  • 2
    This is a great case for implementing a custom directive. You may find instructions how to do it, and in-depth bits about this case here: https://indepth.dev/tutorials/angular/guide-to-repeat-HTML-elements-multiple-times Implementing a custom directive means, that components will have less code, the HTML will be easier to read, and finally, there would be fewer code duplications. The logic in the directive will be easier to test and understand. – Maciej Wojcik Jul 23 '22 at 11:45

17 Answers17

244
<ng-container *ngFor="let _ of [].constructor(20)"></ng-container>

generates

ttulka
  • 10,309
  • 7
  • 41
  • 52
  • 20
    This should be the correct answer.. It's by far the most concise! – Mantisimo Jun 28 '19 at 14:09
  • 1
    ERROR RangeError: Invalid array length. This will crash when the number of cats to draw is zero. – Tom Bevelander Nov 27 '19 at 22:09
  • Really like that simple approach! – F. Geißler Dec 03 '19 at 23:43
  • 4
    It's actually better to use `_` for the variable name since `i` ends up as `undefined` (because the array is empty). If you actually need the index you can use `*ngFor="let _ of [].constructor(20); let catNumber=index"` then `Cat number {{ catNumber + 1 }} ` will show you the cat number. If you have nested loops you can use `_` for each and they won't conflict. – Simon_Weaver Aug 25 '21 at 07:41
  • 3
    Doesn't anybody feel like this is somewhat hacky? I mean, it works well, of course, but creating an array just to be able to replicate content seems not very functional or extensible IMO – Pizzicato May 03 '22 at 12:03
107

You could use the following:

@Component({
  (...)
  template: `
    <div *ngFor="let i of Arr(num).fill(1)"></div>
  `
})
export class SomeComponent {
  Arr = Array; //Array type captured in a variable
  num:number = 20;
}

Or implement a custom pipe:

import {PipeTransform, Pipe} from '@angular/core';

@Pipe({
  name: 'fill'
})
export class FillPipe implements PipeTransform {
  transform(value) {
    return (new Array(value)).fill(1);
  }
}

@Component({
  (...)
  template: `
    <div *ngFor="let i of num | fill"></div>
  `,
  pipes: [ FillPipe ]
})
export class SomeComponent {
  arr:Array;
  num:number = 20;
}
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
95
<div *ngFor="let dummy of ' '.repeat(20).split(''), let x = index">

Replace 20 with your variable

Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Aximili
  • 28,626
  • 56
  • 157
  • 216
66

There are two problems with the recommended solutions using Arrays:

  1. It's wasteful. In particular for large numbers.
  2. You have to define them somewhere which results in a lot of clutter for such a simple and common operation.

It seems more efficient to define a Pipe (once), returning an Iterable:

import {PipeTransform, Pipe} from '@angular/core';

@Pipe({name: 'times'})
export class TimesPipe implements PipeTransform {
  transform(value: number): any {
    const iterable = <Iterable<any>> {};
    iterable[Symbol.iterator] = function* () {
      let n = 0;
      while (n < value) {
        yield ++n;
      }
    };
    return iterable;
  }
}

Usage example (rendering a grid with dynamic width / height):

<table>
    <thead>
      <tr>
        <th *ngFor="let x of colCount|times">{{ x }}</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let y of rowCount|times">
        <th scope="row">{{ y }}</th>
        <td *ngFor="let x of colCount|times">
            <input type="checkbox" checked>
        </td>
      </tr>
    </tbody>
</table>
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Andreas Baumgart
  • 2,647
  • 1
  • 25
  • 20
30

You can simple do this in your HTML:

*ngFor="let number of [0,1,2,3,4,5...,18,19]"

And use the variable "number" to index.

Dekonunes
  • 559
  • 1
  • 5
  • 10
14

A simpler and a reusable solution maybe to use custom structural directive like this.

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appTimes]'
})
export class AppTimesDirective {

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef) { }

  @Input() set appTimes(times: number) {
    for (let i = 0 ; i < times ; i++) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    }
  }

}

And use it like this :

<span *appTimes="3" class="fa fa-star"></span>
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Ilyass Lamrani
  • 149
  • 1
  • 3
8

Best and simple way of doing nth time repetition is [].constructor(nth)

Example for 5 times loop

 <ng-container *ngFor="let _ of [].constructor(5); let i = index">
    <b>{{ i }}</b>
 </ng-container>
user8462556
  • 369
  • 6
  • 13
4

The most efficient and concise way to achieve this is by adding an iterator utility. Don't bother yielding values. Don't bother setting a variable in the ngFor directive:

function times(max: number) {
  return {
    [Symbol.iterator]: function* () {
      for (let i = 0; i < max; i++, yield) {
      }
    }
  };
}

@Component({
  template: ```
<ng-template ngFor [ngForOf]="times(6)">
  repeats 6 times!
</ng-template>

```
})
export class MyComponent {
  times = times;
}
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Amit Portnoy
  • 5,957
  • 2
  • 29
  • 30
3

You don't need to fill the array like suggested in most answers. If you use index in your ngFor loop all you need to create is an empty array with the correct length:

const elements = Array(n); // n = 20 in your case

and in your view:

<li *ngFor="let element in elements; let i = index">
  <span>{{ i }}</span>
</li>
Wilt
  • 41,477
  • 12
  • 152
  • 203
3

I know you specifically asked to do it using *ngFor, but i wanted to share the way i solved this using an structural directive:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[appRepeat]' })
export class RepeatDirective {
  constructor(private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef) {
  }

  @Input() set appRepeat(loops: number) {
    for (let index = 0; index < loops; ++index) {
      this.viewContainerRef.createEmbeddedView(this.templateRef);
    }
  }
}

With that you can use it just like this:

<div *appRepeat="15">
  Testing
</div>
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
  • That's what I was looking for! – Sampgun Aug 16 '21 at 14:52
  • Code could be shorter though...here's my version `@Input() set smpClone(loops: number) { while (--loops > 0) { this._viewContainerRef.createEmbeddedView(this._templateRef); } } ` – Sampgun Aug 16 '21 at 14:57
  • but since we are producing readable code and do not play code golf I would prefer the former solution, although you are technically correct :D – Chund Mar 21 '23 at 13:31
2

If you are using Lodash, you can do the following:

Import Lodash into your component.

import * as _ from "lodash";

Declare a member variable within the component to reference Lodash.

lodash = _;

Then in your view, you can use the range function. 20 can be replaced by any variable in your component.

*ngFor="let number of lodash.range(20)"

It must be said that binding to functions in the view might be costly, depending on the complexity of the function you are calling as Change Detection will call the function repeatedly.

Davy
  • 103
  • 1
  • 6
2

You can use this simply:

HTML

<div *ngFor="let i of Count">

TS

export class Component implements OnInit {
  Count = [];

  constructor() {
    this.Count.length = 10; //you can give any number
  }

  ngOnInit(): void {}
}
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Janitha Rasanga
  • 1,010
  • 1
  • 11
  • 20
0

Simpler approach:

Define a helperArray and instantiate it dynamically (or static if you want) with the length of the count that you want to create your HTML elements. For example, I want to get some data from server and create elements with the length of the array that is returned.

export class AppComponent {
  helperArray: Array<any>;

  constructor(private ss: StatusService) {
  }

  ngOnInit(): void {
    this.ss.getStatusData().subscribe((status: Status[]) => {
      this.helperArray = new Array(status.length);
    });
  }
}

Then use the helperArray in my HTML template.

<div class="content-container" *ngFor="let i of helperArray">
  <general-information></general-information>
  <textfields></textfields>
</div>
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
Florian Leitgeb
  • 15,657
  • 6
  • 31
  • 40
0

Here's a slightly improved version of Ilyass Lamrani's structural directive that allows you to use the index in your template:

@Directive({
  selector: '[appRepeatOf]'
})
export class RepeatDirective {

  constructor(private templateRef: TemplateRef<any>,
              private viewContainer: ViewContainerRef) {
  }

  @Input()
  set appRepeatOf(times: number) {
    const initialLength = this.viewContainer.length;
    const diff = times - initialLength;

    if (diff > 0) {
      for (let i = initialLength; i < initialLength + diff; i++) {
        this.viewContainer.createEmbeddedView(this.templateRef, {
          $implicit: i
        });
      }
    } else {
      for (let i = initialLength - 1; i >= initialLength + diff ; i--) {
      this.viewContainer.remove(i);
    }
  }

}

Usage:

<li *appRepeat="let i of myNumberProperty">
    Index: {{i}}
</li>
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
MK10
  • 311
  • 3
  • 6
0

You can do this :

Ts:

   CreateTempArray(number){
   var arr=[];
   for(let i=0;i<number;i++){
     arr[i]="";
   }
   return arr;
  }

Html:

<div *ngFor="let i of CreateTempArray(20);">
    cycle 20 times
</div>
0

A variation of the top answer

@Component({
  selector: 'app-loop',
  template: `
    <ng-template ngFor [ngForOf]="repeat(20)"></ng-template>
  `
})
export class LoopComponent {
  protected readonly repeat = Array;
}

This displays:

Kevin Baker
  • 569
  • 5
  • 8
0

Super simple solution if you want a number from ts file so you can later on add it into the input is:

In your component.ts file:

amountOfRepetetions = new Array(20);

And in your html template (component.html) component place something like:

<ng-container *ngFor="let oneComponent of amountOfRepetetions">
    <component-i-want-multiplied></component-i-want-multiplied>
</ng-container>

Note: works in Angular 15 (I didn't try it for other versions)

Etaos
  • 640
  • 7
  • 8