Répétez l'élément HTML plusieurs fois en utilisant ngFor en fonction d'un nombre

Réponses:

90

Vous pouvez utiliser les éléments suivants:

@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 implémentez un tube personnalisé:

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
la source
1
la classe FillPipe doit implémenter PipeTransform
toumir
1
Oui tu as raison! C'est mieux ;-) Merci de l'avoir signalé. J'ai mis à jour ma réponse en conséquence ...
Thierry Templier
6
Dans la première approche, je pense que vous vouliez dire arr=Array;?
Abdulrahman Alsoghayer
3
pouvez-vous faire un codepen? cela ne fonctionne pas: self.parentView.context.arr n'est pas une fonction
Toolkit
1
Merci pour la première approche! Je n'utilise pas le .fill (1) et je travaille;)
gtamborero
76
<div *ngFor="let dummy of ' '.repeat(20).split(''), let x = index">

Remplacez 20par votre variable

Aximili
la source
2
C'est certainement une excellente réponse
edkeveked
la répétition doit être 19; longueur-1.
Mert Mertce
Solution élégante pour un problème assez peu pratique. Mais je suppose que cela a un impact sur les performances pour un grand nombre d'éléments?
Martin Eckleben
76
<ng-container *ngFor="let i of [].constructor(20)">🐱</ng-container>

génère 🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱🐱

ttulka
la source
11
Cela devrait être la bonne réponse. C'est de loin la plus concise!
Mantisimo
ERROR RangeError: longueur de tableau non valide. Cela plantera lorsque le nombre de chats à dessiner sera nul.
Tom Bevelander
J'aime vraiment cette approche simple!
F. Geißler
51

Il y a deux problèmes avec les solutions recommandées en utilisant Arrays:

  1. C'est du gaspillage. En particulier pour les grands nombres.
  2. Vous devez les définir quelque part, ce qui entraîne beaucoup d'encombrement pour une opération aussi simple et courante.

Il semble plus efficace de définir un Pipe(une fois), en retournant 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;
  }
}

Exemple d'utilisation (rendu d'une grille avec largeur / hauteur dynamique):

<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
la source
26

Vous pouvez simplement le faire dans votre HTML:

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

Et utilisez la variable "nombre" pour indexer.

Dekonunes
la source
1
OP dit qu'il a assigné 20à une variable membre .. donc cela n'aidera pas beaucoup
phil294
Et si je veux répéter 200 fois?
Chax
1
@Chax Vous ne pouvez pas. :(
Muhammad bin Yusrat
2
@Chax Quel est le problème avec 200? *ngFor="let number of [0,1,2,3,4,5...,199,200]":-D
Stack Underflow
1
@StackUnderflow Malheureusement, je ne suis pas payé par les personnages. Si c'était le cas, je prêcherais que c'est le seul moyen d'y parvenir: P (sérieusement, ne le faites pas;))
Chax
10

Une solution plus simple et réutilisable peut-être d'utiliser une directive structurelle personnalisée comme celle-ci.

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

}

Et utilisez-le comme ceci:

<span *appTimes="3" class="fa fa-star"></span>
Ilyass Lamrani
la source
pour plus d'informations: netbasal.com
Ilyass Lamrani
4

Le moyen le plus efficace et le plus concis d'y parvenir est d'ajouter un utilitaire d'itération. Ne vous embêtez pas à donner des valeurs. Ne vous embêtez pas à définir une variable dans la directive 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
la source
1

Si vous utilisez Lodash , vous pouvez effectuer les opérations suivantes:

Importez Lodash dans votre composant.

import * as _ from "lodash";

Déclarez une variable membre dans le composant pour référencer Lodash.

lodash = _;

Ensuite, dans votre vue, vous pouvez utiliser la fonction de plage . 20 peut être remplacé par n'importe quelle variable de votre composant.

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

Il faut dire que la liaison à des fonctions dans la vue peut être coûteuse, selon la complexité de la fonction que vous appelez, car la détection de changement appellera la fonction à plusieurs reprises.

Davy
la source
1

Vous n'avez pas besoin de remplir le tableau comme suggéré dans la plupart des réponses. Si vous utilisez index dans votre ngForboucle, il vous suffit de créer un tableau vide avec la longueur correcte:

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

et à votre avis:

<li *ngFor="let element in elements; let i = index">
  <span>{{ i }}</span>
</li>
Se flétrir
la source
0

Approche plus simple:

Définissez un helperArray et instanciez-le dynamiquement (ou statique si vous le souhaitez) avec la longueur du nombre que vous souhaitez créer vos éléments HTML. Par exemple, je souhaite obtenir des données du serveur et créer des éléments avec la longueur du tableau renvoyé.

export class AppComponent {
  helperArray: Array<any>;

  constructor(private ss: StatusService) {
  }

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

Ensuite, utilisez le helperArray dans mon modèle HTML.

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

Voici une version légèrement améliorée de la directive structurelle d'Ilyass Lamrani qui vous permet d'utiliser l'index dans votre modèle:

@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>
MK10
la source
0

Je sais que vous avez spécifiquement demandé de le faire en utilisant * ngFor, mais je voulais partager la façon dont j'ai résolu cela en utilisant une directive structurelle:

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

Avec cela, vous pouvez l'utiliser comme ceci:

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

Vous pouvez utiliser ceci simplement:

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