Repita el elemento HTML varias veces usando ngFor basado en un número

Respuestas:

90

Puedes usar lo siguiente:

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

O implementar una tubería personalizada:

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
fuente
1
la clase FillPipe debe implementar PipeTransform
toumir
1
¡Sí tienes razón! Es mejor ;-) Gracias por señalar esto. Actualicé mi respuesta en consecuencia ...
Thierry Templier
6
En el primer enfoque, creo que quiso decir arr=Array;?
Abdulrahman Alsoghayer
3
¿Puedes hacer un codepen? no funciona: self.parentView.context.arr no es una función
Toolkit
1
¡Gracias por el primer acercamiento! No estoy usando el .fill (1) y está funcionando;)
gtamborero
76
<div *ngFor="let dummy of ' '.repeat(20).split(''), let x = index">

Reemplazar 20con su variable

Aximili
fuente
2
Esta es definitivamente una gran respuesta
edkeveked
repetir debe ser 19; longitud-1.
Mert Mertce
Solución elegante para un problema poco conveniente. Pero supongo que tiene un impacto en el rendimiento de una gran cantidad de elementos.
Martin Eckleben
76
<ng-container *ngFor="let i of [].constructor(20)">🐱</ng-container>

genera 🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱

ttulka
fuente
11
Esta debería ser la respuesta correcta. ¡Es, con mucho, la más concisa!
Mantisimo
ERROR RangeError: longitud de matriz no válida. Esto se bloqueará cuando el número de gatos para dibujar sea cero.
Tom Bevelander
¡Realmente me gusta ese enfoque simple!
F. Geißler
51

Hay dos problemas con las soluciones recomendadas que utilizan Arrays:

  1. Es un desperdicio. En particular para grandes cantidades.
  2. Tiene que definirlos en algún lugar, lo que resulta en mucho desorden para una operación tan simple y común.

Parece más eficiente definir a Pipe(una vez), devolviendo un 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;
  }
}

Ejemplo de uso (renderizado de una cuadrícula con ancho / alto dinámico):

<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
fuente
26

Puede hacer esto en su HTML:

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

Y use la variable "número" para indexar.

Dekonunes
fuente
1
OP dijo que le asigna 20a una variable miembro .. así que esto no ayudará mucho
phil294
¿Y si quiero iterar 200 veces?
Chax
1
@Chax No puedes. :(
Muhammad bin Yusrat
2
@Chax ¿Qué pasa con 200? *ngFor="let number of [0,1,2,3,4,5...,199,200]":-D
Stack Underflow
1
@StackUnderflow Lamentablemente, no me pagan los personajes. Si lo fuera, predicaría que es la única manera de lograr eso: P (en serio, simplemente no lo hagas;))
Chax
10

Una solución más simple y reutilizable tal vez sea usar una directiva estructural 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);
    }
  }

}

Y utilícelo así:

<span *appTimes="3" class="fa fa-star"></span>
Ilyass Lamrani
fuente
para más información: netbasal.com/…
Ilyass Lamrani
4

La forma más eficiente y concisa de lograr esto es agregando una utilidad de iterador. No se moleste en ceder valores. No se moleste en establecer una variable en la directiva 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
fuente
1

Si está utilizando Lodash , puede hacer lo siguiente:

Importe Lodash a su componente.

import * as _ from "lodash";

Declare una variable miembro dentro del componente para hacer referencia a Lodash.

lodash = _;

Luego, en su opinión, puede usar la función de rango . 20 puede ser reemplazado por cualquier variable en su componente.

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

Debe decirse que la vinculación a funciones en la vista puede ser costosa, dependiendo de la complejidad de la función que está llamando, ya que Change Detection llamará a la función repetidamente.

Davy
fuente
1

No necesita completar la matriz como se sugiere en la mayoría de las respuestas. Si usa index en su ngForbucle, todo lo que necesita crear es una matriz vacía con la longitud correcta:

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

y en tu opinión:

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

Enfoque más simple:

Defina un helperArray y ejecútelo dinámicamente (o estático si lo desea) con la longitud del recuento que desea para crear sus elementos HTML. Por ejemplo, quiero obtener algunos datos del servidor y crear elementos con la longitud de la matriz que se devuelve.

export class AppComponent {
  helperArray: Array<any>;

  constructor(private ss: StatusService) {
  }

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

Luego use helperArray en mi plantilla HTML.

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

Aquí hay una versión ligeramente mejorada de la directiva estructural de Ilyass Lamrani que le permite usar el índice en su plantilla:

@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
fuente
0

Sé que pidió específicamente hacerlo usando * ngFor, pero quería compartir la forma en que resolví esto usando una directiva estructural:

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

Con eso puedes usarlo así:

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

Puedes usar esto simplemente:

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
fuente