Jak iterować przy użyciu mapy pętli ngFor zawierającej klucz jako ciąg i wartości jako iterację mapy

109

Jestem nowy w Angular 5 i próbuję iterować mapę zawierającą inną mapę w maszynopisie. Jak iterować poniżej tego rodzaju mapy w układzie kątowym poniżej jest kod dla komponentu:

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

@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.css']
})
export class MapComponent implements OnInit {
  map = new Map<String, Map<String,String>>();
  map1 = new Map<String, String>();

  constructor() { 


  }

  ngOnInit() {
    this.map1.set("sss","sss");
    this.map1.set("aaa","sss");
    this.map1.set("sass","sss");
    this.map1.set("xxx","sss");
    this.map1.set("ss","sss");


    this.map1.forEach((value: string, key: string) => {
      console.log(key, value);

    });


    this.map.set("yoyoy",this.map1);

  }



}

a jego szablon html to:

<ul>
  <li *ngFor="let recipient of map.keys()">
    {{recipient}}
   </li>


</ul>

<div>{{map.size}}</div>

błąd wykonania

Feroz Siddiqui
źródło
jakie jest rozwiązanie, którego nie chcę konwertować mapy na tablicę
Feroz Siddiqui,
dzięki działa bardzo dobrze
Feroz Siddiqui

Odpowiedzi:

246

Dla Angular 6.1+ możesz użyć domyślnego potoku keyvalue( wykonaj przegląd i głosuj za ):

<ul>
    <li *ngFor="let recipient of map | keyvalue">
        {{recipient.key}} --> {{recipient.value}}
    </li>
</ul>

PRACY DEMO


W przypadku poprzedniej wersji:

Jednym prostym rozwiązaniem jest przekonwertowanie mapy na tablicę: Array.from

Strona komponentu:

map = new Map<String, String>();

constructor(){
    this.map.set("sss","sss");
    this.map.set("aaa","sss");
    this.map.set("sass","sss");
    this.map.set("xxx","sss");
    this.map.set("ss","sss");
    this.map.forEach((value: string, key: string) => {
        console.log(key, value);
    });
}

getKeys(map){
    return Array.from(map.keys());
}

Strona szablonu:

<ul>
  <li *ngFor="let recipient of getKeys(map)">
    {{recipient}}
   </li>
</ul>

PRACY DEMO

Vivek Doshi
źródło
1
if map = new Map <String, Map <String, String >> (); jak to działa?
Feroz Siddiqui,
@FerozSiddiqui, zaktualizowałem odpowiedź i myślę, że będzie to działać na każdym poziomie zagnieżdżonym.
Vivek Doshi,
straciliśmy całą funkcję mapy, jeśli ostatecznie przekonwertujemy ją na Array. jaka jest zatem korzyść z Map?
diEcho
1
@ pro.mean, konwertujemy tylko dlatego, że chcemy przechodzić przez niego w pętli za pomocą szablonu kątowego, ponieważ wymaga iteracji do iteracji. i nadal masz mapdostępny oryginalny obiekt, możesz go użyć, aby wykorzystać wszystkie zalety mapy
Vivek Doshi,
1
@ pro.mean, dokonaliśmy konwersji tylko po to, aby pokazać to po stronie szablonu, ale nadal masz obaject mapy, którego możesz użyć po stronie komponentu. możesz poprosić OP why he needed this kind of requirement, on może ci lepiej wytłumaczyć :)
Vivek Doshi
48

Jeśli używasz Angular 6.1 lub nowszej, najwygodniej jest użyć KeyValuePipe

   @Component({
      selector: 'keyvalue-pipe',
      template: `<span>
        <p>Object</p>
        <div *ngFor="let item of object | keyvalue">
          {{item.key}}:{{item.value}}
        </div>
        <p>Map</p>
        <div *ngFor="let item of map | keyvalue">
          {{item.key}}:{{item.value}}
        </div>
      </span>`
    })
    export class KeyValuePipeComponent {
      object: Record<number, string> = {2: 'foo', 1: 'bar'};
      map = new Map([[2, 'foo'], [1, 'bar']]);
    }
Londeren
źródło
3
Mam nadzieję, że ludzie przewijają i zobaczą tę odpowiedź, ponieważ miałem zamiar zrobić refaktor, dopóki nie zobaczyłem tej map*ngFor
gotowej
2
wygląda na to, że keyvalue nie zachowuje początkowego sortowania mapy :-(, musisz dodać funkcję
porównującą,
1
To prawda. A oto problem github.com/angular/angular/issues/31420
Londeren
24

Edytować

W przypadku kątowej wersji 6.1 i nowszych użyj KeyValuePipe zgodnie z sugestią Londeren.

Dla kątowych 6.0 i starszych

Dla ułatwienia możesz stworzyć rurę.

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({name: 'getValues'})
export class GetValuesPipe implements PipeTransform {
    transform(map: Map<any, any>): any[] {
        let ret = [];

        map.forEach((val, key) => {
            ret.push({
                key: key,
                val: val
            });
        });

        return ret;
    }
}

 <li *ngFor="let recipient of map |getValues">

Ponieważ jest czysty, nie będzie wyzwalany przy każdym wykryciu zmiany, ale tylko wtedy, gdy mapzmieni się odniesienie do zmiennej

Demo Stackblitz

David
źródło
Myślę, że twoja rura zostanie sprawdzona pod kątem zmian i zrobi pełną iterację w każdym cyklu wykrywania zmian, ponieważ nie jest „czysta”. Nie oddałem głosu, ale może dlatego?
Drenai
5
Rury @Ryan są domyślnie czyste. Jeśli dodasz do potoku plik console.log, zobaczysz, że nie jest on wywoływany wiele razy. Ale metoda komponentowa w przyjętym rozwiązaniu robi…
David,
1
Mam to do tyłu! Dzięki za
ostrzeżenie
10

Dzieje się tak, ponieważ map.keys()zwraca iterator. *ngFormoże współpracować z iteratorami, ale map.keys()będą wywoływane przy każdym cyklu wykrywania zmian, tworząc w ten sposób nowe odniesienie do tablicy, powodując wyświetlany błąd. Nawiasem mówiąc, nie zawsze jest to błąd, jak byś o tym tradycyjnie myślał; może nawet nie zepsuć żadnej z twoich funkcji, ale sugeruje, że masz model danych, który wydaje się zachowywać się w szalony sposób - zmienia się szybciej niż detektor zmian sprawdza jego wartość.

Jeśli nie chcesz konwertować mapy na tablicę w swoim komponencie, możesz użyć potoku sugerowanego w komentarzach. Jak się wydaje, nie ma innego obejścia.

PS Ten błąd nie pojawi się w trybie produkcyjnym, ponieważ jest bardziej ostrym ostrzeżeniem niż rzeczywistym błędem, ale pozostawienie go nie jest dobrym pomysłem.

Armen Vardanyan
źródło
1

Jak ludzie wspomnieli w komentarzach, potok keyvalue nie zachowuje kolejności wstawiania (co jest głównym celem Map).

W każdym razie, wygląda na to, że jeśli masz obiekt Map i chcesz zachować kolejność, najczystszym sposobem na to jest funkcja entry ():

<ul>
    <li *ngFor="let item of map.entries()">
        <span>key: {{item[0]}}</span>
        <span>value: {{item[1]}}</span>
    </li>
</ul>
Wildhammer
źródło
Zobacz github.com/angular/angular/issues/31420#issuecomment-635377113, dlaczego powinieneś unikać używania .entries ()
Florian
1

Poniższy kod przydatny do wyświetlenia w zamówieniu reklamowym mapy .

<ul>
    <li *ngFor="let recipient of map | keyvalue: asIsOrder">
        {{recipient.key}} --> {{recipient.value}}
    </li>
</ul>

Plik .ts dodaj poniższy kod.

asIsOrder(a, b) {
    return 1;
}
Shakthifuture
źródło