Angolare 2 - NgPer usare numeri invece di raccolte


191

...per esempio...

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

È possibile fare qualcosa come ...

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

... senza appello a una soluzione non elegante come:

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

?


8
Ho lo stesso problema. Davvero sconvolto non si possono fare cose così semplici con l'angolare 2.
albanx

1
Forse questo può essere utile: stackoverflow.com/questions/3895478/…
Pizzicato

Risposte:


198

All'interno del componente, è possibile definire una matrice di numeri (ES6) come descritto di seguito:

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

Vedere questo collegamento per la creazione di array: il modo più semplice per creare un array di numeri interi da 1 a 20 in JavaScript .

È quindi possibile scorrere su questo array con ngFor:

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

O a breve:

@Component({
  template: `
    <ul>
      <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
    </ul>
  `
})
export class SampleComponent {
  (...)
}

5
Sì, Thierry! Non è colpa tua, anzi, ma è sempre nello stesso contesto :( Non è affatto elegante. Ma dato che sei uno sviluppatore A2 molto abile, posso presumere che non ci sia soluzione migliore. È triste!
Marco Jr

In effetti, non c'è nulla per questo in Angular2 nella sintassi del loop. È necessario sfruttare ciò che JavaScript fornisce per creare array. Ad esempio: Array(5).fill(4)creare[4,4,4,4,4]
Thierry Templier

3
PS: l'annotazione @View è stata rimossa in angular2 beta 10 e successive.
Pardeep Jain,

23
L'uso Array.fill()in Angular 2 Typescript produce il seguente errore Supplied parameters do not match any signature of call t arget.: controllo dei documenti Array.prototype.fill, dice che richiede 1 argomento ... 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]*/questo risolve l'errore in TS
mshahbazm

90

@OP, eri incredibilmente vicino alla tua soluzione "non elegante".

Che ne dite di:

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

Qui sto ottenendo il Arraycostruttore da un array vuoto:, [].constructorperché Arraynon è un simbolo riconosciuto nella sintassi del modello, e sono troppo pigro per fare Array=Arrayo counter = Arraynel dattiloscritto componente come ha fatto @ pardeep-jain nel suo quarto esempio. E lo sto chiamando senza newperché newnon è necessario per ottenere un array dal Arraycostruttore.

Array(30)e new Array(30)sono equivalenti.

L'array sarà vuoto, ma non importa perché vuoi davvero usarlo idal ;let i = indextuo loop.


13
Questa è la risposta migliore
Kagronick,

Questa soluzione attiva il rilevamento delle modifiche. Immagino grazie al nuovo array.
Tobias81,

1
@ Tobias81, potresti elaborare? Stai dicendo che ogni volta che l'app esegue il rilevamento delle modifiche, il contenuto di * ngPer viene ridisegnato perché l'array viene ricreato? Vale sicuramente la pena notare. Si potrebbe aggirare il problema creando effettivamente un campo array nel TS per fare riferimento in modo che sia lo stesso ogni volta che viene eseguito il rilevamento delle modifiche. Ma sarebbe sicuramente meno elegante del desiderato. Lo stesso problema di rilevamento delle modifiche è presente nel secondo esempio nella risposta selezionata di Thierry Templier? <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
jcairney,

questa è la migliore soluzione trovata per questo problema
khush

1
@ Tobias81, ho verificato che il rilevamento delle modifiche non ricrea ripetutamente il contenuto di ngFor, inserendo un'istruzione print nel costruttore di un componente che creo come figlio dell'esempio della direttiva ngFor. Non vedo i componenti ricreati su ogni iterazione di Rilevamento modifiche, quindi non penso che ci sia effettivamente un problema (almeno in Angular 8).
jcairney,

83

No, non esiste ancora un metodo per NgFor che utilizza numeri anziché raccolte. Al momento, * ngFor accetta solo una raccolta come parametro, ma è possibile farlo seguendo i seguenti metodi:

Usando la pipa

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>

Utilizzo dell'array numerico direttamente in HTML (Visualizza)

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

Utilizzando il metodo Split

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

Utilizzo della creazione di un nuovo array nel componente

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

Demo funzionante


4
È inoltre possibile utilizzare il Array.fill()metodo per generare l'array anziché res.push()come mostrato nella risposta di Thierrys.
Günter Zöchbauer,

si posso ma c'è qualcosa che non va push? intendo dire che entrambi i metodi sono corretti ma comunque se diff. tra loro.
Pardeep Jain,

3
No, resta comunque una bella soluzione +1. Trovo solo il Array.fill()più elegante del loop usando push ed è probabilmente anche più efficiente.
Günter Zöchbauer,

1
Mi piace questa soluzione con counter = Array, molto intelligente;)
Verri

11

Non potevo sopportare l'idea di allocare un array per la semplice ripetizione dei componenti, quindi ho scritto una direttiva strutturale. Nella forma più semplice, che non rende l'indice disponibile per il modello, si presenta così:

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


Concordo sul fatto che l'approccio array è brutto, ma a me sembra un'ottimizzazione prematura.
Aluan Haddad,

3
Certo, ma anche un esercizio per scrivere una direttiva. D'altra parte non è più lungo del tubo, che sarebbe un approccio di seconda scelta.
pdudits

Questo è un buon punto, non ci sono molte opportunità per ottenere alcuni dei tuoi con il concetto di direttive strutturali personalizzate.
Aluan Haddad,

Nice one @pdudits - Funziona ancora con le ultime versioni: plnkr.co/edit/8wJtkpzre3cBNokHcDL7?p=preview [sentiti libero di aggiornare il tuo plnkr]
AL

5

L'ho risolto in questo modo usando Angular 5.2.6 e 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);
}

Può essere utilizzato in un componente come questo:

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

Controllo degli errori e asserzioni omesse appositamente per brevità (ad es. Cosa succede se il passo è negativo).


2
Ci sono modi in html come <div *ngfor="let i of 4, i++"></div>potrebbe essere
Nithila Shanmugananthan

5

puoi anche usare così

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>

4

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

Non ho testato il codice ma dovrebbe funzionare.


Ci sono modi in html come <div *ngfor="let i of 4, i++"></div>potrebbe essere
Nithila Shanmugananthan

Se hai bisogno io indicizzi un codice, puoi farlo*ngFor="let i of range; let i = index"
Alex Po,

3

Questo può essere ottenuto anche in questo modo:

HTML:

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

Dattiloscritto:

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

Demo di lavoro


2

Poiché il metodo fill () (menzionato nella risposta accettata) senza argomenti genera un errore, suggerirei qualcosa del genere (funziona per me, Angular 7.0.4, Typescript 3.1.6)

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

Nel codice componente:

this.items = Array.from({length: 10}, (v, k) => k + 1);

1
<div *ngFor="let number of [].constructor(myCollection)">
    <div>
        Hello World
    </div>
</div>

Questo è un modo semplice e veloce per ripetere per la quantità di volte in myCollection.

Quindi se myCollection fosse 5, Hello World verrebbe ripetuto 5 volte.


1

Utilizzo della direttiva strutturale personalizzata con indice:

Secondo la documentazione angolare:

createEmbeddedView Crea un'istanza di una vista incorporata e la inserisce in questo contenitore.

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.

Quando angolare crea un modello chiamando createEmbeddedView, può anche passare il contesto che verrà utilizzato all'interno ng-template.

Utilizzando il parametro facoltativo di contesto, è possibile utilizzarlo nel componente, estraendolo all'interno del modello proprio come si farebbe con * 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));
  }
}

0

Si prega di trovare la mia soluzione dinamica allegata se si desidera aumentare dinamicamente le dimensioni di un array dopo aver fatto clic su un pulsante (Ecco come sono arrivato a questa domanda).

Allocazione delle variabili necessarie:

  array = [1];
  arraySize: number;

Dichiarare la funzione che aggiunge un elemento all'array:

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

Richiama la funzione in html

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

Scorrere l'array con ngPer:

<div *ngFor="let i of array" >
  iterateThroughArray: {{ i }}
</div>

Ci sono modi in html come <div *ngfor="let i of 4, i++"></div>potrebbe essere
Nithila Shanmugananthan

devi iterare su un array. Se è necessario lo scalare, è possibile scorrere su un array con le dimensioni giuste e creare un'istanza di uno scalare in aggiunta: * ngFor = "let item of array; let i = index"
Jan Clemens Stoffregen

0

Un modo più semplice che ho provato

Puoi anche creare un array nel tuo file componente e puoi chiamarlo con la direttiva * ngFor ritornando come un array.

Qualcosa come questo ....

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() {
  }

}

E questa funzione può essere utilizzata nel file modello html

<p *ngFor="let a of arra(); let i= index">
value:{{a}} position:{{i}}
</p>

2
Ci sono modi in html come <div *ngfor="let i of 4, i++"></div>potrebbe essere
Nithila Shanmugananthan

0

La mia soluzione:

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

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

questo crea un nuovo array ad ogni get. Potrebbe creare overhead
Remco Vlierman il
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.