Ripeti l'elemento HTML più volte utilizzando ngFor in base a un numero


Risposte:


90

Potresti usare quanto segue:

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

Oppure implementa una pipe personalizzata:

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

1
la classe FillPipe deve implementare PipeTransform
toumir

1
Sì hai ragione! È meglio ;-) Grazie per averlo segnalato. Ho aggiornato di conseguenza la mia risposta ...
Thierry Templier

6
Nel primo approccio, penso che volevi dire arr=Array;?
Abdulrahman Alsoghayer

3
puoi fare un codepen? non funziona: self.parentView.context.arr non è una funzione
Toolkit

1
Grazie per il primo approccio! Non sto usando il .fill (1) e funziona;)
gtamborero

76
<div *ngFor="let dummy of ' '.repeat(20).split(''), let x = index">

Sostituisci 20con la tua variabile


2
Questa è sicuramente un'ottima risposta
edkeveked il

ripetere dovrebbe essere 19; lunghezza-1.
Mert Mertce

Soluzione elegante per un problema abbastanza scomodo. Ma immagino che abbia un impatto sulle prestazioni per un gran numero di elementi?
Martin Eckleben

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

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


11
Questa dovrebbe essere la risposta corretta .. È di gran lunga la più concisa!
Mantisimo

ERRORE RangeError: lunghezza array non valida. Questo andrà in crash quando il numero di gatti da disegnare è zero.
Tom Bevelander

Mi piace davvero quel semplice approccio!
F. Geißler

51

Esistono due problemi con le soluzioni consigliate utilizzando Arrays :

  1. È uno spreco. In particolare per grandi numeri.
  2. Devi definirli da qualche parte, il che si traduce in un sacco di disordine per un'operazione così semplice e comune.

Sembra più efficiente definire un Pipe(una volta), restituendo 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;
  }
}

Esempio di utilizzo (rendering di una griglia con larghezza / altezza dinamica):

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

26

Puoi farlo semplicemente nel tuo HTML:

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

E usa la variabile "numero" per indicizzare.


1
OP ha detto di aver assegnato 20a una variabile membro .. quindi questo non aiuterà molto
phil294

E se volessi ripetere 200 volte?
Chax

1
@ Chax Non puoi. :(
Muhammad bin Yusrat,

2
@ Chax Cosa c'è che non va in 200? *ngFor="let number of [0,1,2,3,4,5...,199,200]":-D
Stack Underflow

1
@StackUnderflow Purtroppo non sono pagato dai personaggi. Se lo fossi, predicherei che è l'unico modo per ottenere che: P (seriamente semplicemente non farlo;))
Chax

10

Una soluzione più semplice e riutilizzabile forse per utilizzare direttive strutturali personalizzate come questa.

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 usalo in questo modo:

<span *appTimes="3" class="fa fa-star"></span>

per maggiori informazioni: netbasal.com/…
Ilyass Lamrani

4

Il modo più efficiente e conciso per ottenere ciò è aggiungere un'utilità iteratore. Non preoccuparti di cedere valori. Non preoccuparti di impostare una variabile nella direttiva 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;
}

1

Se stai usando Lodash , puoi fare quanto segue:

Importa Lodash nel tuo componente.

import * as _ from "lodash";

Dichiarare una variabile membro all'interno del componente per fare riferimento a Lodash.

lodash = _;

Quindi, a tuo avviso, puoi utilizzare la funzione di intervallo . 20 può essere sostituito da qualsiasi variabile nel componente.

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

Va detto che l'associazione alle funzioni nella vista potrebbe essere costosa, a seconda della complessità della funzione che si sta chiamando poiché Change Detection chiamerà la funzione ripetutamente.


1

Non è necessario riempire la matrice come suggerito nella maggior parte delle risposte. Se usi index nel tuo ngForloop, tutto ciò che devi creare è un array vuoto con la lunghezza corretta:

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

e secondo te:

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

0

Approccio più semplice:

Definire un helperArray e istanziarlo dinamicamente (o statico se lo si desidera) con la lunghezza del conteggio a cui si desidera creare gli elementi HTML. Ad esempio, voglio ottenere alcuni dati dal server e creare elementi con la lunghezza dell'array restituito.

export class AppComponent {
  helperArray: Array<any>;

  constructor(private ss: StatusService) {
  }

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

Quindi usa l'helperArray nel mio modello HTML.

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

0

Ecco una versione leggermente migliorata della direttiva strutturale di Ilyass Lamrani che ti consente di utilizzare l'indice nel tuo modello:

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

}

Utilizzo:

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

0

So che hai specificamente chiesto di farlo usando * ngFor, ma volevo condividere il modo in cui ho risolto questo problema usando una direttiva strutturale:

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 quello puoi usarlo proprio in questo modo:

<div *appRepeat="15">
  Testing
</div>

0

Puoi usarlo semplicemente:

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 {}
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.