Winkel 2 - NgFür die Verwendung von Zahlen anstelle von Sammlungen

190

...beispielsweise...

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

Ist möglich so etwas wie ...

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

... ohne Appell an eine nicht elegante Lösung wie:

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

?

Marco Jr.
quelle
8
Ich habe das gleiche Problem. Wirklich verärgert kann man mit eckigen 2 nicht so einfache Dinge tun.
Albanx
1
Vielleicht kann dies hilfreich sein: stackoverflow.com/questions/3895478/…
Pizzicato

Antworten:

195

Innerhalb Ihrer Komponente können Sie ein Array von Zahlen (ES6) wie folgt definieren:

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

Informationen zur Array-Erstellung finden Sie unter diesem Link: Die engste Methode zum Erstellen eines Arrays von Ganzzahlen aus 1..20 in JavaScript .

Sie können dann über dieses Array iterieren mit ngFor:

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

Oder kurz:

@Component({
  template: `
    <ul>
      <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
    </ul>
  `
})
export class SampleComponent {
  (...)
}
Thierry Templier
quelle
5
Ja, Thierry! Es ist in der Tat nicht deine Schuld, aber immer noch im selben Kontext :( Es ist überhaupt nicht elegant. Aber da du ein sehr erfahrener A2-Entwickler bist, kann ich davon ausgehen, dass es keine bessere Lösung gibt. Es ist traurig!
Marco Jr
Tatsächlich gibt es in Angular2 in der Schleifensyntax nichts dafür. Sie müssen die Vorteile von JavaScript nutzen, um Arrays zu erstellen. Zum Beispiel: Array(5).fill(4)zu erstellen[4,4,4,4,4]
Thierry Templier
3
PS: @ View-Annotation wurde in Angular2 Beta 10 und höher entfernt.
Pardeep Jain
23
Die Verwendung Array.fill()in Angular 2 Typescript führt zu folgendem Fehler Supplied parameters do not match any signature of call t arget.: Wenn Sie die Dokumente Array.prototype.fill überprüfen, wird 1 Argument benötigt ... 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]*/Dies behebt den Fehler in TS
mshahbazm
85

@OP, Sie waren mit Ihrer "nicht eleganten" Lösung furchtbar nah dran.

Wie wäre es mit:

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

Hier erhalte ich den ArrayKonstruktor aus einem leeren Array : [].constructor, weil Arrayes in der Vorlagensyntax kein erkanntes Symbol ist und ich zu faul bin, dies zu tun, Array=Arrayoder counter = Arrayim Komponententypskript, wie es @ pardeep-jain in seinem vierten Beispiel getan hat. Und ich rufe es ohne auf, newweil newes nicht notwendig ist, ein Array aus dem ArrayKonstruktor herauszuholen .

Array(30)und new Array(30)sind gleichwertig.

Das Array wird leer sein, aber das spielt keine Rolle , weil Sie wirklich nur verwenden möchten ivon ;let i = indexin der Schleife.

jcairney
quelle
11
Dies ist die beste Antwort.
Kagronick
Diese Lösung löst die Änderungserkennung aus. Ich denke aufgrund des neuen Arrays.
Tobias81
1
@ Tobias81, könntest du das näher erläutern? Wollen Sie damit sagen, dass jedes Mal, wenn die App die Änderungserkennung ausführt, der Inhalt von * ngFor neu gezeichnet wird, weil das Array neu erstellt wird? Das ist definitiv erwähnenswert. Man könnte es umgehen, indem man tatsächlich ein Array-Feld im TS erstellt, auf das verwiesen wird, damit es bei jeder Änderungserkennung gleich ist. Aber das wäre definitiv weniger elegant als gewünscht. Ist das gleiche Problem bei der Änderungserkennung im zweiten Beispiel in der ausgewählten Antwort von Thierry Templier vorhanden? <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
JCairney
Dies ist die beste Lösung für dieses Problem
Khush
1
@ Tobias81, ich habe überprüft, ob die Änderungserkennung den Inhalt von ngFor nicht wiederholt neu erstellt, indem ich eine print-Anweisung in den Konstruktor einer Komponente eingefügt habe, die ich als untergeordnetes Element der Beispielanweisung ngFor erstellt habe. Ich sehe nicht, dass die Komponenten bei jeder Iteration der Änderungserkennung neu erstellt werden, daher glaube ich nicht, dass tatsächlich ein Problem vorliegt (zumindest in Angular 8).
JCairney
83

Nein, es gibt noch keine Methode für NgFor, bei der Zahlen anstelle von Sammlungen verwendet werden. Derzeit akzeptiert * ngFor nur eine Sammlung als Parameter. Sie können dies jedoch mit den folgenden Methoden tun:

Rohr verwenden

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>

Verwenden des Zahlenarrays direkt in HTML (Ansicht)

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

Verwenden der Split-Methode

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

Verwenden des Erstellens eines neuen Arrays in einer Komponente

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

Arbeitsdemo

Pardeep Jain
quelle
4
Sie können auch die Array.fill()Methode zum Generieren des Arrays verwenden, anstatt res.push()wie in Thierrys Antwort gezeigt.
Günter Zöchbauer
Ja, ich kann, aber stimmt etwas nicht push? Ich meine, beide Methoden sind korrekt, aber immer noch, wenn es Unterschiede gibt. zwischen ihnen.
Pardeep Jain
3
Nein, immer noch eine schöne Lösung +1. Ich finde das Array.fill()eleganter als die Schleife mit Push und es ist wahrscheinlich auch effizienter.
Günter Zöchbauer
1
Ich mag diese Lösung mit counter = Array, sehr klug;)
Verri
11

Ich konnte die Idee nicht ertragen, ein Array für die einfache Wiederholung von Komponenten zuzuweisen, deshalb habe ich eine strukturelle Direktive geschrieben. In der einfachsten Form, die den Index für die Vorlage nicht verfügbar macht, sieht es folgendermaßen aus:

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
quelle
Ich bin damit einverstanden, dass der Array-Ansatz hässlich ist, aber dies scheint mir eine vorzeitige Optimierung zu sein.
Aluan Haddad
3
Natürlich, aber auch eine Übung zum Schreiben einer Richtlinie. Auf der anderen Seite ist es nicht länger als das Rohr, was ein zweiter vernünftiger Ansatz wäre.
pdudits
Das ist ein guter Punkt, es gibt nicht viele Möglichkeiten, einige von Ihnen mit dem Konzept der benutzerdefinierten strukturellen Richtlinien vertraut zu machen.
Aluan Haddad
Nice one @pdudits - funktioniert immer noch mit den neuesten Versionen: plnkr.co/edit/8wJtkpzre3cBNokHcDL7?p=preview [fühlen sich frei , Ihre plnkr zu aktualisieren]
AT
5

Ich habe es so gelöst mit Angular 5.2.6 und 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);
}

Es kann in einer Komponente wie der folgenden verwendet werden:

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

Fehlerprüfung und Behauptungen wurden der Kürze halber absichtlich weggelassen (z. B. was passiert, wenn der Schritt negativ ist).

Per Edin
quelle
2
Gibt es irgendwelche Möglichkeiten in HTML wie <div *ngfor="let i of 4, i++"></div>möglich
Nithila Shanmugananthan
5

Sie können auch so verwenden

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

Sie können lodash verwenden:

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

Ich habe keinen Code getestet, aber es sollte funktionieren.

Alex Po
quelle
Gibt es irgendwelche Möglichkeiten in HTML wie <div *ngfor="let i of 4, i++"></div>möglich
Nithila Shanmugananthan
Wenn Sie ieinen Code benötigen oder indizieren, können Sie dies tun*ngFor="let i of range; let i = index"
Alex Po
3

Dies kann auch folgendermaßen erreicht werden:

HTML:

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

Typoskript:

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

Arbeitsdemo

Adrita Sharma
quelle
2

Da die fill () -Methode (in der akzeptierten Antwort erwähnt) ohne Argumente einen Fehler auslöst, würde ich so etwas vorschlagen (funktioniert für mich, Angular 7.0.4, Typescript 3.1.6).

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

Im Komponentencode:

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

Dies ist eine schöne und schnelle Möglichkeit, die Anzahl der Male in myCollection zu wiederholen.

Wenn myCollection also 5 wäre, würde Hello World 5 Mal wiederholt.

Jake Mahy
quelle
1

Verwenden einer benutzerdefinierten Strukturrichtlinie mit Index:

Laut Angular-Dokumentation:

createEmbeddedView Instanziiert eine eingebettete Ansicht und fügt sie in diesen Container ein.

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.

Wenn Angular eine Vorlage durch Aufrufen von createEmbeddedView erstellt, kann auch der Kontext übergeben werden, der im Inneren verwendet wird ng-template.

Mit dem optionalen Kontextparameter können Sie ihn in der Komponente verwenden und ihn wie mit * ngFor in der Vorlage extrahieren.

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

Im Anhang finden Sie meine dynamische Lösung, wenn Sie die Größe eines Arrays nach dem Klicken auf eine Schaltfläche dynamisch erhöhen möchten (so bin ich zu dieser Frage gekommen).

Zuordnung der notwendigen Variablen:

  array = [1];
  arraySize: number;

Deklarieren Sie die Funktion, die dem Array ein Element hinzufügt:

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

Rufen Sie die Funktion in HTML auf

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

Durch das Array mit ngFor iterieren:

<div *ngFor="let i of array" >
  iterateThroughArray: {{ i }}
</div>
Jan Clemens Stoffregen
quelle
Gibt es irgendwelche Möglichkeiten in HTML wie <div *ngfor="let i of 4, i++"></div>möglich
Nithila Shanmugananthan
Sie müssen über ein Array iterieren. Wenn Sie den Skalar benötigen, können Sie über ein Array mit der richtigen Größe iterieren und zusätzlich einen Skalar instanziieren: * ngFor = "Element des Arrays lassen; i = Index lassen"
Jan Clemens Stoffregen
0

Ein einfachster Weg, den ich versucht habe

Sie können auch ein Array in Ihrer Komponentendatei erstellen und es mit der Direktive * ngFor aufrufen, indem Sie als Array zurückkehren.

Etwas wie das ....

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

}

Und diese Funktion kann in Ihrer HTML-Vorlagendatei verwendet werden

<p *ngFor="let a of arra(); let i= index">
value:{{a}} position:{{i}}
</p>
Alok Panday
quelle
2
Gibt es irgendwelche Möglichkeiten in HTML wie <div *ngfor="let i of 4, i++"></div>möglich
Nithila Shanmugananthan
0

Meine Lösung:

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>
cuddlemeister
quelle
Dadurch wird bei jedem Abruf ein neues Array erstellt. Könnte Overhead schaffen
Remco Vlierman