Kątowy 2 - Ng Do używania liczb zamiast kolekcji

191

...na przykład...

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

Można zrobić coś takiego jak ...

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

... bez odwołania do nie eleganckich rozwiązań takich jak:

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

?

Marco Jr
źródło
8
Mam ten sam problem. Naprawdę zdenerwowany nie można robić tak prostych rzeczy za pomocą kątownika 2.
albanx
1
Może to może być pomocne: stackoverflow.com/questions/3895478/…
Pizzicato

Odpowiedzi:

197

W ramach komponentu możesz zdefiniować tablicę liczb (ES6), jak opisano poniżej:

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

Zobacz ten link do tworzenia tablicy: Najkrótszy sposób na utworzenie tablicy liczb całkowitych od 1..20 w JavaScript .

Następnie możesz iterować tę tablicę za pomocą ngFor:

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

Lub krótko:

@Component({
  template: `
    <ul>
      <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
    </ul>
  `
})
export class SampleComponent {
  (...)
}
Thierry Templier
źródło
5
Tak, Thierry! Rzeczywiście, to nie twoja wina, ale nadal w tym samym kontekście :( To wcale nie jest eleganckie. Ale ponieważ jesteś bardzo wykwalifikowanym programistą A2, mogę założyć, że nie ma lepszego rozwiązania. To smutne!
Marco Jr
W rzeczywistości nie ma na to nic w Angular2 w składni pętli. Musisz wykorzystać to, co zapewnia JavaScript, aby budować tablice. Na przykład: Array(5).fill(4)aby utworzyć[4,4,4,4,4]
Thierry Templier
3
PS: Adnotacja @View została usunięta w wersji kątowej 2 beta 10 i nowszych.
Pardeep Jain
23
Używanie Array.fill()w Angular 2 Typescript powoduje następujący błąd Supplied parameters do not match any signature of call t arget.- Sprawdzanie dokumentów Array.prototype.fill, mówi, że wymaga 1 argumentu ... 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]*/rozwiązuje to błąd w TS
mshahbazm
90

@OP, byłeś okropnie blisko ze swoim „nie eleganckim” rozwiązaniem.

Co powiesz na:

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

Tutaj pobieram Arraykonstruktor z pustej tablicy :, [].constructorponieważ Arraynie jest rozpoznanym symbolem w składni szablonu, i jestem zbyt leniwy, aby to zrobić Array=Arraylub counter = Arrayw skrypcie komponentu, jak @ pardeep-jain w swoim czwartym przykładzie. I nazywam to bez, newponieważ newnie jest to konieczne do uzyskania tablicy z Arraykonstruktora.

Array(30)i new Array(30)są równoważne.

Tablica będzie pusta, ale to nie ma znaczenia, ponieważ naprawdę chcesz po prostu korzystać iz ;let i = indexpętli.

Jcairney
źródło
12
To najlepsza odpowiedź.
kagronick
To rozwiązanie uruchamia wykrywanie zmian. Chyba ze względu na nowy Array.
Tobias81,
1
@ Tobias81, czy mógłbyś opracować? Czy mówisz, że za każdym razem, gdy aplikacja uruchamia wykrywanie zmian, zawartość * ngFor jest przerysowywana, ponieważ tablica jest odtwarzana? To zdecydowanie warte odnotowania. Można to obejść, tworząc pole tablicy w TS, do którego ma się odwoływać, więc jest takie samo przy każdym uruchomieniu wykrywania zmian. Ale to byłoby zdecydowanie mniej eleganckie niż pożądane. Czy ten sam problem z wykrywaniem zmian występuje w drugim przykładzie w wybranej odpowiedzi Thierry Templier? <li *ngFor="let number of [0,1,2,3,4]">{{number}}</li>
jcairney
jest to najlepsze rozwiązanie dla tego problemu
khush
1
@ Tobias81, sprawdziłem, czy wykrywanie zmian nie odtwarza wielokrotnie zawartości ngFor, umieszczając instrukcję konstruktora w konstruktorze komponentu, który tworzę jako element potomny przykładowej dyrektywy ngFor. Nie widzę, że komponenty są odtwarzane przy każdej iteracji Wykrywania Zmian, więc nie sądzę, aby rzeczywiście istniał problem (przynajmniej w Angular 8).
jcairney
83

Nie, nie ma jeszcze metody dla NgFor do używania liczb zamiast kolekcji. W tej chwili * ngFor akceptuje tylko kolekcję jako parametr, ale można to zrobić za pomocą następujących metod:

Za pomocą rury

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>

Używanie tablicy liczb bezpośrednio w HTML (Zobacz)

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

Korzystanie z metody Split

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

Korzystanie z tworzenia nowej tablicy w komponencie

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

Działające demo

Pardeep Jain
źródło
4
Możesz także użyć Array.fill()metody do generowania tablicy zamiast res.push()takiej, jak pokazano w odpowiedzi Thierrys.
Günter Zöchbauer
tak, mogę, ale czy coś jest nie tak push? Mam na myśli, że obie metody są poprawne, ale nadal, jeśli jakieś różnice. między nimi.
Pardeep Jain,
3
Nie, wciąż dobre rozwiązanie +1. Po prostu uważam, że jest Array.fill()bardziej elegancki niż pętla za pomocą push i jest też prawdopodobnie bardziej wydajny.
Günter Zöchbauer
1
Podoba mi się to rozwiązanie counter = Array, bardzo inteligentne;)
Verri
11

Nie mogłem znieść pomysłu przydzielenia tablicy do zwykłego powtarzania komponentów, dlatego napisałem dyrektywę strukturalną. W najprostszej formie, która nie udostępnia indeksu szablonowi, wygląda to tak:

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
źródło
Zgadzam się, że podejście tablicowe jest brzydkie, ale wydaje mi się to przedwczesną optymalizacją.
Aluan Haddad
3
Oczywiście, ale także ćwiczenie w pisaniu dyrektywy. Z drugiej strony nie jest on dłuższy niż rura, co byłoby drugim rozsądnym podejściem.
pdudits
To dobra uwaga, nie ma wielu okazji, aby zdobyć trochę z koncepcją niestandardowych dyrektyw strukturalnych.
Aluan Haddad
Nice one @pdudits - Nadal działa z najnowszymi wersjami: plnkr.co/edit/8wJtkpzre3cBNokHcDL7?p=preview [nie wahaj się zaktualizować swojego plnkr]
AT
5

Rozwiązałem to w ten sposób, używając Angulara 5.2.6 i 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);
}

Można go użyć w komponencie takim jak ten:

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

Sprawdzanie błędów i oświadczenia celowo pominięte ze względu na zwięzłość (np. Co się stanie, jeśli krok jest negatywny).

Per Edin
źródło
2
Czy są jakieś sposoby, poczynając od html <div *ngfor="let i of 4, i++"></div>może być
Nithila Shanmugananthan
5

możesz także użyć w ten sposób

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
źródło
4

Możesz użyć 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);
}

Nie testowałem kodu, ale powinien działać.

Alex Po
źródło
Czy są jakieś sposoby w html na dzień <div *ngfor="let i of 4, i++"></div>może być
Nithila Shanmugananthan
Jeśli potrzebujesz ilub zindeksujesz kod, możesz to zrobić*ngFor="let i of range; let i = index"
Alex Po
3

Można to również osiągnąć w następujący sposób:

HTML:

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

Maszynopis:

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

Demo pracy

Adrita Sharma
źródło
2

Ponieważ metoda fill () (wymieniona w zaakceptowanej odpowiedzi) bez argumentów generuje błąd, sugerowałbym coś takiego (działa dla mnie, Angular 7.0.4, Typescript 3.1.6)

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

W kodzie komponentu:

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

Jest to przyjemny i szybki sposób na powtarzanie ilości razy w myCollection.

Więc jeśli myCollection miałoby 5 lat, Hello World byłby powtarzany 5 razy.

Jake Mahy
źródło
1

Używanie niestandardowej dyrektywy strukturalnej z indeksem:

Zgodnie z dokumentacją kątową:

createEmbeddedView Tworzy instancję osadzonego widoku i wstawia go do tego kontenera.

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.

Kiedy angular tworzy szablon, wywołując createEmbeddedView, może również przekazać kontekst, który będzie używany w środku ng-template.

Używając opcjonalnego parametru kontekstowego, możesz go użyć w komponencie, wyodrębniając go w szablonie, tak jak w przypadku * 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
źródło
0

Proszę załączyć moje dynamiczne rozwiązanie, jeśli chcecie dynamicznie zwiększać rozmiar tablicy po kliknięciu przycisku (oto jak dostałem się do tego pytania).

Przydział niezbędnych zmiennych:

  array = [1];
  arraySize: number;

Zadeklaruj funkcję, która dodaje element do tablicy:

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

Wywołaj funkcję w HTML

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

Iteruj po tablicy za pomocą ngFor:

<div *ngFor="let i of array" >
  iterateThroughArray: {{ i }}
</div>
Jan Clemens Stoffregen
źródło
Czy są jakieś sposoby, poczynając od html <div *ngfor="let i of 4, i++"></div>może być
Nithila Shanmugananthan
musisz iterować po tablicy. Jeśli potrzebujesz skalaru, możesz iterować tablicę o odpowiednim rozmiarze i dodatkowo utworzyć instancję skalara: * ngFor = "let item of array; let i = index"
Jan Clemens Stoffregen
0

Najprostszy sposób, który próbowałem

Możesz także utworzyć tablicę w pliku komponentu i wywołać ją za pomocą dyrektywy * ngFor, zwracając jako tablicę.

Coś takiego ....

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

}

Tej funkcji można użyć w pliku szablonu HTML

<p *ngFor="let a of arra(); let i= index">
value:{{a}} position:{{i}}
</p>
Alok Panday
źródło
2
Czy są jakieś sposoby, poczynając od html <div *ngfor="let i of 4, i++"></div>może być
Nithila Shanmugananthan
0

Moje rozwiązanie:

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

W 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
źródło
tworzy to nową tablicę przy każdym pobraniu. Można utworzyć koszty ogólne
Remco Vlierman