Repita o elemento HTML várias vezes usando ngFor com base em um número

103

Como faço *ngForpara repetir um elemento HTML várias vezes?

Por exemplo: Se eu tiver uma variável de membro atribuída a 20. Como uso a diretiva * ngFor para fazer uma div repetir 20 vezes?

takeradi
fonte
1
Consulte também stackoverflow.com/questions/47586525/…
yurzui 01 de
Existem quatro maneiras de conseguir o mesmo, leia aqui. stackoverflow.com/a/36356629/5043867
Pardeep Jain

Respostas:

90

Você pode usar o seguinte:

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

Ou implemente um tubo personalizado:

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;
}
Thierry Templier
fonte
1
a classe FillPipe deve implementar PipeTransform
toumir
1
Sim, você está certo! É melhor ;-) Obrigado por apontar isso. Atualizei minha resposta de acordo ...
Thierry Templier
6
Na primeira abordagem, acho que você quis dizer arr=Array;?
Abdulrahman Alsoghayer
3
você pode fazer um codepen? não funciona: self.parentView.context.arr não é uma função
Toolkit
1
Obrigado pela primeira abordagem! Não estou usando o .fill (1) e está funcionando;)
gtamborero
76
<div *ngFor="let dummy of ' '.repeat(20).split(''), let x = index">

Substitua 20pela sua variável

Aximili
fonte
2
Esta é definitivamente uma ótima resposta
edkeveked em
repetir deve ser 19; comprimento-1.
Mert Mertce
Solução elegante para um problema pouco conveniente. Mas eu acho que isso tem um impacto no desempenho de uma grande quantidade de elementos.
Martin Eckleben
76
<ng-container *ngFor="let i of [].constructor(20)">🐱</ng-container>

gera 🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱

ttulka
fonte
11
Esta deve ser a resposta correta. É de longe a mais concisa!
Mantisimo
ERROR RangeError: Comprimento de array inválido. Isso irá travar quando o número de gatos a desenhar for zero.
Tom Bevelander
Realmente gosto dessa abordagem simples!
F. Geißler
51

Existem dois problemas com as soluções recomendadas usando Arrays:

  1. É um desperdício. Em particular para grandes números.
  2. Você tem que defini-los em algum lugar, o que resulta em muita desordem para uma operação tão simples e comum.

Parece mais eficiente definir um Pipe(uma vez), retornando um 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;
  }
}

Exemplo de uso (renderização de uma grade com largura / altura dinâmica):

<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>
Andreas Baumgart
fonte
26

Você pode fazer isso simplesmente em seu HTML:

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

E use a variável "número" para indexar.

Dekonunes
fonte
1
OP disse que atribuiu 20a uma variável de membro .. então isso não vai ajudar muito
phil294
E se eu quiser iterar 200 vezes?
Chax
1
@Chax Você não pode. :(
Muhammad bin Yusrat
2
@Chax O que há de errado com 200? *ngFor="let number of [0,1,2,3,4,5...,199,200]":-D
Stack Underflow
1
@StackUnderflow Infelizmente, não sou pago por personagens. Se eu fosse, pregaria que essa é a única maneira de conseguir isso: P (sério, simplesmente não faça;))
Chax
10

Uma solução mais simples e reutilizável talvez seja usar uma diretiva estrutural personalizada como esta.

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

}

E use-o assim:

<span *appTimes="3" class="fa fa-star"></span>
Ilyass Lamrani
fonte
para mais informações: netbasal.com/…
Ilyass Lamrani
4

A maneira mais eficiente e concisa de conseguir isso é adicionando um utilitário iterador. Não se preocupe em ceder valores. Não se preocupe em definir uma variável na diretiva ngFor:

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;
}
Amit Portnoy
fonte
1

Se estiver usando Lodash , você pode fazer o seguinte:

Importe Lodash em seu componente.

import * as _ from "lodash";

Declare uma variável de membro dentro do componente para fazer referência a Lodash.

lodash = _;

Então, em sua visão, você pode usar a função de intervalo . 20 pode ser substituído por qualquer variável em seu componente.

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

Deve-se dizer que vincular a funções na visualização pode ser caro, dependendo da complexidade da função que você está chamando, pois a Detecção de Mudança chamará a função repetidamente.

Davy
fonte
1

Você não precisa preencher a matriz como sugerido na maioria das respostas. Se você usar o índice em seu ngForloop, tudo o que você precisa criar é uma matriz vazia com o comprimento correto:

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

e na sua opinião:

<li *ngFor="let element in elements; let i = index">
  <span>{{ i }}</span>
</li>
Wilt
fonte
0

Abordagem mais simples:

Defina um helperArray e instancie-o dinamicamente (ou estático, se desejar) com o comprimento da contagem que você deseja para criar seus elementos HTML. Por exemplo, desejo obter alguns dados do servidor e criar elementos com o comprimento do array que é retornado.

export class AppComponent {
  helperArray: Array<any>;

  constructor(private ss: StatusService) {
  }

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

Em seguida, use o helperArray em meu modelo HTML.

<div class="content-container" *ngFor="let i of helperArray">
  <general-information></general-information>
  <textfields></textfields>
</div>
Florian Leitgeb
fonte
0

Esta é uma versão ligeiramente melhorada da diretiva estrutural de Ilyass Lamrani que permite usar o índice em seu modelo:

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

}

Uso:

<li *appRepeat="let i of myNumberProperty">
    Index: {{i}}
</li>
MK10
fonte
0

Sei que você pediu especificamente para fazer isso usando * ngFor, mas gostaria de compartilhar a maneira como resolvi isso usando uma diretiva estrutural:

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

Com isso, você pode usá-lo assim:

<div *appRepeat="15">
  Testing
</div>
Javier Campón
fonte
0

Você pode usar isso simplesmente:

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 {}
}
Janitha Rasanga
fonte