Angular 2 - NgPour utiliser des nombres à la place des collections

191

...par exemple...

<div class="month" *ngFor="#item of myCollection; #i = index">
...
</div>

Est-il possible de faire quelque chose comme ...

<div class="month" *ngFor="#item of 10; #i = index">
...
</div>

... sans appel à une solution non élégante comme:

<div class="month" *ngFor="#item of ['dummy','dummy','dummy','dummy','dummy',
'dummy','dummy','dummy']; #i = index">
...
</div>

?

Marco Jr
la source
8
J'ai le même problème. Vraiment contrarié, on ne peut pas faire des choses aussi simples avec angular 2.
albanx
1
Peut-être que cela peut être utile: stackoverflow.com/questions/3895478/…
Pizzicato

Réponses:

198

Dans votre composant, vous pouvez définir un tableau de nombres (ES6) comme décrit ci-dessous:

export class SampleComponent {
  constructor() {
    this.numbers = Array(5).fill().map((x,i)=>i); // [0,1,2,3,4]
    this.numbers = Array(5).fill(4); // [4,4,4,4,4]
  }
}

Voir ce lien pour la création du tableau: Méthode d'essai pour créer un tableau d'entiers de 1..20 en JavaScript .

Vous pouvez ensuite parcourir ce tableau avec ngFor:

@Component({
  template: `
    <ul>
      <li *ngFor="let number of numbers">{{number}}</li>
    </ul>
  `
})
export class SampleComponent {
  (...)
}

Ou brièvement:

@Component({
  template: `
    <ul>
      <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
    </ul>
  `
})
export class SampleComponent {
  (...)
}
Thierry Templier
la source
5
Ouais, Thierry! Ce n'est pas de votre faute, en effet, mais toujours dans le même contexte :( Ce n'est pas du tout élégant. Mais comme vous êtes un développeur A2 très habile, je peux supposer qu'il n'y a pas de meilleure solution. C'est triste!
Marco Jr
En fait, il n'y a rien pour cela dans Angular2 dans la syntaxe de la boucle. Vous devez tirer parti de ce que fournit JavaScript pour créer des tableaux. Par exemple: Array(5).fill(4)créer[4,4,4,4,4]
Thierry Templier
3
PS: l'annotation @View a été supprimée dans angular2 beta 10 et supérieur.
Pardeep Jain
23
L'utilisation Array.fill()dans Angular 2 Typescript produit l'erreur suivante Supplied parameters do not match any signature of call t arget.- En vérifiant la documentation Array.prototype.fill, elle indique qu'il faut 1 argument ... developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Joshua Russell
5
Array(5).fill(1).map((x, i) => i + 1); /*[1,2,3,4,5]*/cela résout l'erreur dans TS
mshahbazm
90

@OP, vous étiez terriblement proche de votre solution "non élégante".

Que diriez-vous:

<div class="month" *ngFor="let item of [].constructor(10); let i = index"> ... </div>

Ici, j'obtiens le Arrayconstructeur à partir d'un tableau vide:, [].constructorcar ce Arrayn'est pas un symbole reconnu dans la syntaxe du modèle, et je suis trop paresseux pour le faire Array=Arrayou counter = Arraydans le script de composant comme @ pardeep-jain l'a fait dans son 4ème exemple. Et je l'appelle sansnew parce que ce newn'est pas nécessaire pour sortir un tableauArray constructeur.

Array(30) et new Array(30) sont équivalents.

Le tableau sera vide, mais cela n'a pas d'importance car vous voulez vraiment utiliser ifrom ;let i = indexdans votre boucle.

jcairney
la source
13
C'est la meilleure réponse.
kagronick
Cette solution déclenche la détection des changements. Je suppose que grâce au nouveau Array.
Tobias81
1
@ Tobias81, pourriez-vous élaborer? Êtes-vous en train de dire que chaque fois que l'application exécute la détection des modifications, le contenu de * ngFor est redessiné car le tableau est recréé? Cela vaut vraiment la peine d'être noté. On pourrait le contourner en créant un champ de tableau dans le TS à référencer afin qu'il soit le même à chaque fois que la détection de changement s'exécute. Mais ce serait certainement moins élégant que souhaité. Le même problème de détection de changement est-il présent dans le 2ème exemple de la réponse choisie par Thierry Templier? <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
jcairney le
c'est la meilleure solution trouvée pour ce problème
khush
1
@ Tobias81, j'ai vérifié pour m'assurer que la détection de changement ne recrée pas le contenu du ngFor à plusieurs reprises, en mettant une instruction print à l'intérieur du constructeur d'un composant que je crée en tant qu'enfant de l'exemple de directive ngFor. Je ne vois pas les composants recréés à chaque itération de détection de changement, donc je ne pense pas qu'il y ait réellement de problème (au moins dans Angular 8).
jcairney
83

Non, il n'y a pas encore de méthode pour NgFor utilisant des nombres à la place des collections, Pour le moment, * ngFor n'accepte qu'une collection en tant que paramètre, mais vous pouvez le faire en utilisant les méthodes suivantes:

Utilisation du tuyau

pipe.ts

import {Pipe, PipeTransform} from 'angular2/core';

@Pipe({name: 'demoNumber'})
export class DemoNumber implements PipeTransform {
  transform(value, args:string[]) : any {
    let res = [];
    for (let i = 0; i < value; i++) {
        res.push(i);
      }
      return res;
  }
}


<ul>
  <li>Method First Using PIPE</li>
  <li *ngFor='let key of 5 | demoNumber'>
    {{key}}
  </li>
</ul>

Utilisation d'un tableau de nombres directement dans HTML (View)

<ul>
  <li>Method Second</li>
  <li *ngFor='let key of  [1,2]'>
    {{key}}
  </li>
</ul>

Utilisation de la méthode Split

<ul>
  <li>Method Third</li>
  <li *ngFor='let loop2 of "0123".split("")'>{{loop2}}</li>
</ul>

Utilisation de la création d'un nouveau tableau dans le composant

<ul>
  <li>Method Fourth</li>
  <li *ngFor='let loop3 of counter(5) ;let i= index'>{{i}}</li>
</ul>

export class AppComponent {
  demoNumber = 5 ;

  counter = Array;

  numberReturn(length){
    return new Array(length);
  }
}

Démo de travail

Pardeep Jain
la source
4
Vous pouvez également utiliser la Array.fill()méthode de génération du tableau au lieu de res.push()celle indiquée dans la réponse de Thierrys.
Günter Zöchbauer
ouais je peux mais y a-t-il quelque chose qui ne va pas push? Je veux dire que les deux méthodes sont correctes mais toujours si elles diffèrent. entre eux.
Pardeep Jain
3
Non, toujours une belle solution +1. Je trouve juste le Array.fill()plus élégant que la boucle en utilisant push et c'est aussi probablement plus efficace.
Günter Zöchbauer
1
J'aime cette solution avec counter = Array, très intelligente;)
Verri
11

Je ne pouvais pas supporter l'idée d'allouer un tableau pour la simple répétition des composants, j'ai donc écrit une directive structurelle. Dans sa forme la plus simple, cela ne rend pas l'index disponible pour le modèle, cela ressemble à ceci:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[biRepeat]' })
export class RepeatDirective {

  constructor( private templateRef: TemplateRef<any>,
             private viewContainer: ViewContainerRef) { }

  @Input('biRepeat') set count(c:number) {
    this.viewContainer.clear();
    for(var i=0;i<c;i++) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    }
  }
}

http://plnkr.co/edit/bzoNuL7w5Ub0H5MdYyFR?p=preview

pdudits
la source
Je suis d'accord que l'approche par tableau est moche, mais cela me semble être une optimisation prématurée.
Aluan Haddad
3
Bien sûr, mais aussi un exercice de rédaction d'une directive. D'un autre côté, il n'est pas plus long que le tuyau, ce qui serait une deuxième approche sensée.
pdudits
C'est un bon point, il n'y a pas beaucoup d'occasions d'obtenir une partie de votre avec le concept de directives structurelles personnalisées.
Aluan Haddad
Nice one @pdudits - Fonctionne toujours avec les dernières versions: plnkr.co/edit/8wJtkpzre3cBNokHcDL7?p=preview [n'hésitez pas à mettre à jour votre plnkr]
AT
5

Je l'ai résolu comme ceci en utilisant Angular 5.2.6 et TypeScript 2.6.2:

class Range implements Iterable<number> {
    constructor(
        public readonly low: number,
        public readonly high: number,
        public readonly step: number = 1
    ) {
    }

    *[Symbol.iterator]() {
        for (let x = this.low; x <= this.high; x += this.step) {
            yield x;
        }
    }
}

function range(low: number, high: number) {
    return new Range(low, high);
}

Il peut être utilisé dans un composant comme celui-ci:

@Component({
    template: `<div *ngFor="let i of r">{{ i }}</div>`
})
class RangeTestComponent {
    public r = range(10, 20);
}

Vérification des erreurs et affirmations omises volontairement par souci de concision (par exemple, que se passe-t-il si l'étape est négative).

Per Edin
la source
2
Existe-t-il des moyens en html à partir de <div *ngfor="let i of 4, i++"></div>mai
Nithila Shanmugananthan
5

vous pouvez aussi utiliser comme ça

export class SampleComponent {
   numbers:Array<any> = [];
   constructor() {
      this.numbers = Array.from({length:10},(v,k)=>k+1);
   }
}

HTML

<p *ngFor="let i of numbers">
   {{i}}
</p>
Tonmoy Nandy
la source
4

Vous pouvez utiliser lodash:

@Component({
  selector: 'board',
  template: `
<div *ngFor="let i of range">
{{i}}
</div>
`,
  styleUrls: ['./board.component.css']
})
export class AppComponent implements OnInit {
  range = _.range(8);
}

Je n'ai pas testé le code mais cela devrait fonctionner.

Alex Po
la source
Existe-t-il des moyens en html à partir de <div *ngfor="let i of 4, i++"></div>mai
Nithila Shanmugananthan
Si vous avez besoin iou indexez un code, vous pouvez le faire*ngFor="let i of range; let i = index"
Alex Po
3

Ceci peut également être réalisé comme ceci:

HTML:

<div *ngFor="let item of fakeArray(10)">
     ...
</div>

Manuscrit:

fakeArray(length: number): Array<any> {
  if (length >= 0) {
    return new Array(length);
  }
}

Démo de travail

Adrita Sharma
la source
2

Puisque la méthode fill () (mentionnée dans la réponse acceptée) sans arguments jette une erreur, je suggérerais quelque chose comme ça (fonctionne pour moi, Angular 7.0.4, Typescript 3.1.6)

<div class="month" *ngFor="let item of items">
...
</div>

Dans le code composant:

this.items = Array.from({length: 10}, (v, k) => k + 1);
Dalibor
la source
1
<div *ngFor="let number of [].constructor(myCollection)">
    <div>
        Hello World
    </div>
</div>

C'est un moyen agréable et rapide de répéter le nombre de fois dans myCollection.

Donc, si myCollection était de 5, Hello World serait répété 5 fois.

Jake Mahy
la source
1

Utilisation de la directive structurelle personnalisée avec index:

Selon la documentation angulaire:

createEmbeddedView Instancie une vue intégrée et l'insère dans ce conteneur.

abstract createEmbeddedView(templateRef: TemplateRef, context?: C, index?: number): EmbeddedViewRef.

Param          Type           Description
templateRef    TemplateRef    the HTML template that defines the view.
context        C              optional. Default is undefined.
index          number         the 0-based index at which to insert the new view into this container. If not specified, appends the new view as the last entry.

Lorsque angular crée un modèle en appelant createEmbeddedView, il peut également transmettre le contexte qui sera utilisé à l'intérieur ng-template.

En utilisant le paramètre facultatif context, vous pouvez l'utiliser dans le composant, en l'extrayant dans le modèle comme vous le feriez avec le * ngFor.

app.component.html:

<p *for="number; let i=index; let c=length; let f=first; let l=last; let e=even; let o=odd">
  item : {{i}} / {{c}}
  <b>
    {{f ? "First,": ""}}
    {{l? "Last,": ""}}
    {{e? "Even." : ""}}
    {{o? "Odd." : ""}}
  </b>
</p>

for.directive.ts:

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

class Context {
  constructor(public index: number, public length: number) { }
  get even(): boolean { return this.index % 2 === 0; }
  get odd(): boolean { return this.index % 2 === 1; }
  get first(): boolean { return this.index === 0; }
  get last(): boolean { return this.index === this.length - 1; }
}

@Directive({
  selector: '[for]'
})
export class ForDirective {
  constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { }

  @Input('for') set loop(num: number) {
    for (var i = 0; i < num; i++)
      this.viewContainer.createEmbeddedView(this.templateRef, new Context(i, num));
  }
}
Rafi Henig
la source
0

Veuillez trouver ci-joint ma solution dynamique si vous souhaitez augmenter la taille d'un tableau de manière dynamique après avoir cliqué sur un bouton (c'est ainsi que je suis arrivé à cette question).

Attribution des variables nécessaires:

  array = [1];
  arraySize: number;

Déclarez la fonction qui ajoute un élément au tableau:

increaseArrayElement() {
   this.arraySize = this.array[this.array.length - 1 ];
   this.arraySize += 1;
   this.array.push(this.arraySize);
   console.log(this.arraySize);
}

Appelez la fonction en html

  <button md-button (click)="increaseArrayElement()" >
      Add element to array
  </button>

Itérer à travers un tableau avec ngFor:

<div *ngFor="let i of array" >
  iterateThroughArray: {{ i }}
</div>
Jan Clemens Stoffregen
la source
Existe-t-il des moyens en html à partir de <div *ngfor="let i of 4, i++"></div>mai
Nithila Shanmugananthan
vous devez parcourir un tableau. Si vous avez besoin du scalaire, vous pouvez parcourir un tableau avec la bonne taille et instancier un scalaire en plus: * ngFor = "let item of array; let i = index"
Jan Clemens Stoffregen
0

Un moyen le plus simple que j'ai essayé

Vous pouvez également créer un tableau dans votre fichier de composant et vous pouvez l'appeler avec la directive * ngFor en le renvoyant sous forme de tableau.

Quelque chose comme ça ....

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-morning',
  templateUrl: './morning.component.html',
  styleUrls: ['./morning.component.css']
})
export class MorningComponent implements OnInit {

  arr = [];
  i: number = 0;
  arra() {
    for (this.i = 0; this.i < 20; this.i++) {
      this.arr[this.i]=this.i;
    }
    return this.arr;
  }

  constructor() { }

  ngOnInit() {
  }

}

Et cette fonction peut être utilisée dans votre fichier de modèle html

<p *ngFor="let a of arra(); let i= index">
value:{{a}} position:{{i}}
</p>
Alok Panday
la source
2
Existe-t-il des moyens en html à partir de <div *ngfor="let i of 4, i++"></div>mai
Nithila Shanmugananthan
0

Ma solution:

export class DashboardManagementComponent implements OnInit {
  _cols = 5;
  _rows = 10;
  constructor() { }

  ngOnInit() {
  }

  get cols() {
    return Array(this._cols).fill(null).map((el, index) => index);
  }
  get rows() {
    return Array(this._rows).fill(null).map((el, index) => index);
  }

En html:

<div class="charts-setup">
  <div class="col" *ngFor="let col of cols; let colIdx = index">
    <div class="row" *ngFor="let row of rows; let rowIdx = index">
      Col: {{colIdx}}, row: {{rowIdx}}
    </div>
  </div>
</div>
câlin
la source
cela crée un nouveau tableau à chaque obtention. Pourrait créer des frais généraux
Remco Vlierman