So iterieren Sie mit ngFor loop Map, die Schlüssel als Zeichenfolge und Werte als Karteniteration enthält


102

Ich bin neu in Winkel 5 und versuche, die Karte zu wiederholen, die eine andere Karte in Typoskript enthält. Wie man unter dieser Art von Karte im Winkel unten iteriert, ist Code für Komponente:

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

  }



}

und seine Vorlage HTML ist:

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


</ul>

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

Laufzeit Fehler



Was ist die Lösung, die ich nicht meine Karte in Array konvertieren möchte
Feroz Siddiqui


Dank funktioniert sehr gut
Feroz Siddiqui

Antworten:


226

Für Angular 6.1+ können Sie die Standardpipe verwenden keyvalue( überprüfen und bewerten Sie auch ):

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

ARBEITSDEMO


Für die vorherige Version:

Eine einfache Lösung hierfür ist die Konvertierung von Map in Array: Array.from

Komponentenseite:

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

Vorlagenseite:

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

ARBEITSDEMO


1
if map = new Map <String, Map <String, String >> (); wie funktioniert es?
Feroz Siddiqui

@FerozSiddiqui, ich habe die Antwort aktualisiert und denke, dass dies für jede verschachtelte Ebene funktioniert.
Vivek Doshi

Wir haben alle Map-Funktionen verloren, wenn wir sie endgültig in Array konvertieren. Was ist dann der Vorteil von Map?
DiEcho

1
@ pro.mean, wir konvertieren nur, weil wir es über eine eckige Vorlage durchlaufen möchten, da einige iterierbare Elemente iteriert werden sollen. und Sie haben noch Originalobjekt zur mapVerfügung, können Sie dieses verwenden, um alle Vorteile von Map
Vivek Doshi

1
@ pro.mean, wir haben nur konvertiert, um dies auf der Vorlagenseite zu zeigen, aber Sie haben immer noch ein Map-Objekt, das Sie auf Ihrer Komponentenseite verwenden können. Sie können zu OP fragen why he needed this kind of requirement, er kann Sie besser erklären :)
Vivek Doshi

45

Wenn Sie Angular 6.1 oder höher verwenden, ist die Verwendung von KeyValuePipe die bequemste Methode

   @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']]);
    }

3
Hoffentlich scrollen die Leute und sehen diese Antwort, denn ich wollte gerade einen Refactor durchführen, bis ich dieses für meinen mapTyp verfügbare Out-of-the-Box-Rohr gesehen hatte, das sich um die Verwendung mit*ngFor
atconway

2
Es scheint, dass der Schlüsselwert die anfängliche Sortierung der Karte nicht beibehält :-(, Sie müssen eine Vergleichsfunktion hinzufügen, um dies zu beheben
Konstantin Vahrushev

1
Das ist wahr. Und hier ist ein Problem github.com/angular/angular/issues/31420
Londeren

24

Bearbeiten

Für Winkel 6.1 und neuer, verwenden Sie die KeyValuePipe wie Londeren vorgeschlagen.

Für Winkel 6.0 und älter

Zur Vereinfachung können Sie eine Pipe erstellen.

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

Da es rein ist, wird es nicht bei jeder Änderungserkennung ausgelöst, sondern nur, wenn sich der Verweis auf die mapVariable ändert

Stackblitz-Demo


Ich denke, Ihre Pipe wird auf Änderungen überprüft und bei jedem Änderungserkennungszyklus vollständig iteriert, da sie nicht "rein" ist. Nicht abgestimmt, aber vielleicht deshalb?
Drenai

4
@ Ryan Pipes sind standardmäßig rein. Wenn Sie der Pipe ein console.log hinzufügen, werden Sie feststellen, dass es nicht mehrmals aufgerufen wird. Aber Komponentenmethode in der akzeptierten Lösung tut ...
David

1
Ich habe das rückwärts! Vielen Dank für die Heads-Ups
Drenai

10

Dies liegt daran, dass map.keys()ein Iterator zurückgegeben wird. *ngForkann mit Iteratoren arbeiten, map.keys()wird jedoch bei jedem Änderungserkennungszyklus aufgerufen, wodurch eine neue Referenz auf das Array erstellt wird, was zu dem angezeigten Fehler führt. Dies ist übrigens nicht immer ein Fehler, wie Sie es traditionell denken würden. Es kann sogar sein, dass es Ihre Funktionalität nicht beeinträchtigt, deutet jedoch darauf hin, dass Sie ein Datenmodell haben, das sich verrückt zu verhalten scheint - es ändert sich schneller, als der Änderungsdetektor seinen Wert überprüft.

Wenn Sie die Map nicht in ein Array in Ihrer Komponente konvertieren möchten, können Sie die in den Kommentaren vorgeschlagene Pipe verwenden. Wie es scheint, gibt es keine andere Problemumgehung.

PS Dieser Fehler wird im Produktionsmodus nicht angezeigt, da es sich eher um eine sehr strenge Warnung als um einen tatsächlichen Fehler handelt. Dennoch ist es keine gute Idee, ihn zu belassen.


1

Wie bereits in den Kommentaren erwähnt, behält die keyvalue-Pipe nicht die Reihenfolge des Einfügens bei (was der Hauptzweck von Map ist).

Wie auch immer, es sieht so aus, als ob Sie ein Map-Objekt haben und die Reihenfolge beibehalten möchten. Der sauberste Weg, dies zu tun, ist die Funktion entry ():

<ul>
    <li *ngFor="let item of map.entries()">
        <span>key: {{item[0]}}</span>
        <span>value: {{item[1]}}</span>
    </li>
</ul>

Bitte lesen Sie github.com/angular/angular/issues/31420#issuecomment-635377113, warum Sie die Verwendung von .entries () vermeiden sollten
Florian

1

Der folgende Code ist nützlich, um ihn in der Reihenfolge der Karteneinfügung anzuzeigen .

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

.ts-Datei fügen Sie den folgenden Code hinzu.

asIsOrder(a, b) {
    return 1;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.