184

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?

3
  • 1
    See also stackoverflow.com/questions/47586525/… Commented Dec 1, 2017 at 4:55
  • There are four ways to achieve the same, read out here. stackoverflow.com/a/36356629/5043867 Commented Aug 22, 2019 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: indepth.dev/tutorials/angular/… 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. Commented Jul 23, 2022 at 11:45

19 Answers 19

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

generates 🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱

Sign up to request clarification or add additional context in comments.

8 Comments

This should be the correct answer.. It's by far the most concise!
ERROR RangeError: Invalid array length. This will crash when the number of cats to draw is zero.
Really like that simple approach!
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.
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
|
110

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;
}

9 Comments

the FillPipe class must implements PipeTransform
Yes, you're right! It's better ;-) Thanks for pointing this out. I updated my answer accordingly...
In the first approach, I think you meant to say arr=Array; ?
can you make a codepen? it doesn't work: self.parentView.context.arr is not a function
Thanks for the first approach! I am not using the .fill(1) and is working ;)
|
98
<div *ngFor="let dummy of ' '.repeat(20).split(''), let x = index">

Replace 20 with your variable

4 Comments

This is definitely a great answer
repeat should be 19; length-1.
Elegant solution for a quite unconvenient problem. But I guess it has a performance impact for a big count of elements?
this is a great solution.. just that how will we get the value from that? Based on that.. I created <div *ngFor="let dummy of ' '.repeat(5).split(''), let x = index"><input type="text" name="myitem{{x}}" placeholder=" " /></span></div>, for example how will I get the value of myitem2 ?
72

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>

1 Comment

So <ion-row *ngFor="let r of 10|times"> works! Thanks Andreas you've managed to simulate the well known for next loop in HTML :thumbsup
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.

6 Comments

OP said he assigned 20 to a member variable.. so this will not help much
What if i want to iterate 200 times?
@Chax You cant. :(
@Chax What's wrong with 200? *ngFor="let number of [0,1,2,3,4,5...,199,200]" :-D
@StackUnderflow Sadly I'm not paid by characters. If I was, I would preach that it is the only way to acheive that :P (seriously just don't ;))
|
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>

2 Comments

for more info : netbasal.com/…
For this solution, you need to add this.viewContainer.clear(); before the for-loop.
9

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>

Comments

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;
}

Comments

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>

Comments

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>

3 Comments

That's what I was looking for!
Code could be shorter though...here's my version @Input() set smpClone(loops: number) { while (--loops > 0) { this._viewContainerRef.createEmbeddedView(this._templateRef); } }
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
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.

Comments

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 {}
}

Comments

2

with angular 17

@for (i of [].constructor(4); track i) { <div> 🐱 </div>}

2 Comments

I think there is an issue with the track param, as every element is having the same track value
the track is bad, you should track $index instead
1

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)

Comments

1

With new angular @for workflow and correct tracking:

component html:

@for (index of lineIndexes; track index) {
    repeated stuff
}

component ts:

export class MyComponent {
  // generates [0, 1, 2, 3, 4, 5, ... , 18, 19] to use as tracking indexes
  protected lineIndexes = [...Array(20).keys()];
}

Comments

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>

Comments

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>

Comments

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>

Comments

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: 🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭🌭

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.