วิธีการวนซ้ำโดยใช้ ngFor loop Map ที่มีคีย์เป็นสตริงและค่าเป็นแมปซ้ำ


109

ฉันเพิ่งเริ่มใช้ angular 5 และพยายามทำซ้ำแผนที่ที่มีแผนที่อื่นใน typescript วิธีการวนซ้ำด้านล่างของแผนที่ประเภทนี้ในเชิงมุมด้านล่างคือรหัสสำหรับส่วนประกอบ:

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

  }



}

และเทมเพลต html คือ:

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


</ul>

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

การทำงานผิดพลาด



วิธีแก้ปัญหาคืออะไรฉันไม่ต้องการแปลงแผนที่เป็นอาร์เรย์
Feroz Siddiqui


ขอบคุณทำงานได้ดีมาก
Feroz Siddiqui

คำตอบ:


246

สำหรับ Angular 6.1+คุณสามารถใช้ไปป์เริ่มต้นkeyvalue( ตรวจสอบและเพิ่มคะแนนด้วย ):

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

การสาธิตการทำงาน


สำหรับเวอร์ชันก่อนหน้า:

วิธีแก้ปัญหาง่ายๆวิธีหนึ่งคือแปลงแผนที่เป็นอาร์เรย์: Array.from

ด้านส่วนประกอบ:

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

ด้านแม่แบบ:

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

การสาธิตการทำงาน


1
ถ้า map = new Map <String, Map <String, String >> (); มันทำงานอย่างไร?
Feroz Siddiqui

@FerozSiddiqui ฉันได้อัปเดตคำตอบแล้วและฉันคิดว่าจะใช้ได้กับทุกระดับที่ซ้อนกัน
Vivek Doshi

เราสูญเสียคุณสมบัติแผนที่ทั้งหมดหากเราแปลงเป็น Array ในที่สุด แล้ว Map มีประโยชน์อย่างไร?
diEcho

1
@ pro.mean เรากำลังแปลงเพียงเพราะเราต้องการวนซ้ำผ่านเทมเพลตเชิงมุมเนื่องจากต้องการให้ทำซ้ำได้ และคุณยังมีวัตถุดั้งเดิมmapอยู่คุณสามารถใช้วัตถุนั้นเพื่อใช้ประโยชน์ทั้งหมดของแผนที่ได้
Vivek Doshi

1
@ pro.mean เราได้แปลงเพียงเพื่อแสดงให้เห็นว่าในด้านเทมเพลต แต่คุณยังมีวัตถุแผนที่ที่คุณสามารถใช้ในด้านส่วนประกอบของคุณได้ คุณสามารถขอให้ OP why he needed this kind of requirementเขาอธิบายคุณได้ดีกว่า :)
Vivek Doshi

48

หากคุณใช้ Angular 6.1 หรือใหม่กว่าวิธีที่สะดวกที่สุดคือใช้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']]);
    }

3
หวังว่าผู้คนจะเลื่อนดูคำตอบนี้เพราะฉันกำลังจะทำ refactor จนกว่าฉันจะได้เห็นท่อนอกกรอบนี้สำหรับmapประเภทของฉันซึ่งดูแลการใช้งานด้วย*ngFor
atconway

2
ดูเหมือนว่าค่าคีย์ไม่สามารถจัดเรียงแผนที่เริ่มต้นได้ :-( คุณต้องเพิ่มฟังก์ชัน
Comparer

1
นี่คือเรื่องจริง และนี่คือปัญหาgithub.com/angular/angular/issues/31420
Londeren

24

แก้ไข

สำหรับเชิงมุม 6.1 และใหม่กว่าให้ใช้KeyValuePipeตามที่ Londeren แนะนำ

สำหรับเชิงมุม 6.0 ขึ้นไป

เพื่อให้ง่ายขึ้นคุณสามารถสร้างท่อได้

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

ตามที่เป็นจริงจะไม่ถูกทริกเกอร์ในการตรวจจับการเปลี่ยนแปลงทุกครั้ง แต่เฉพาะในกรณีที่การอ้างอิงถึงmapตัวแปรเปลี่ยนแปลงเท่านั้น

การสาธิต Stackblitz


ฉันคิดว่าไปป์ของคุณจะได้รับการตรวจสอบการเปลี่ยนแปลงและทำการตรวจสอบซ้ำทั้งหมดในทุกรอบการตรวจจับการเปลี่ยนแปลงเนื่องจากไม่ใช่ "บริสุทธิ์" ไม่ได้ลงคะแนน แต่นั่นอาจเป็นเหตุผลว่าทำไม?
Drenai

5
@Ryan Pipes บริสุทธิ์ไปโดยปริยาย หากคุณเพิ่ม console.log ในไพพ์คุณจะเห็นว่าไม่ได้รับการโทรหลายครั้ง แต่วิธีส่วนประกอบในโซลูชันที่ยอมรับทำได้ ...
เดวิด

1
ฉันเข้าใจแล้ว! ขอบคุณที่แจ้งให้ทราบ
Drenai

10

เนื่องจากmap.keys()ส่งคืนตัวทำซ้ำ *ngForสามารถทำงานร่วมกับตัวทำซ้ำได้ แต่map.keys()จะเรียกใช้ในทุกรอบการตรวจจับการเปลี่ยนแปลงดังนั้นจึงสร้างการอ้างอิงใหม่ไปยังอาร์เรย์ส่งผลให้เกิดข้อผิดพลาดที่คุณเห็น อย่างไรก็ตามนี่ไม่ใช่ข้อผิดพลาดเสมอไปอย่างที่คุณคิดแบบเดิม ๆ มันอาจไม่ทำลายฟังก์ชันการทำงานใด ๆ ของคุณ แต่แนะนำว่าคุณมีโมเดลข้อมูลที่ดูเหมือนว่าจะทำงานผิดปกติ - เปลี่ยนแปลงเร็วกว่าที่ตัวตรวจจับการเปลี่ยนแปลงจะตรวจสอบค่าของมัน

หากคุณไม่ต้องการแปลงแผนที่เป็นอาร์เรย์ในองค์ประกอบของคุณคุณสามารถใช้ไปป์ที่แนะนำในความคิดเห็น ไม่มีวิธีแก้ปัญหาอื่น ๆ อย่างที่เห็น

ป.ล. ข้อผิดพลาดนี้จะไม่แสดงในโหมดการใช้งานจริงเนื่องจากเป็นเหมือนคำเตือนที่เข้มงวดมากแทนที่จะเป็นข้อผิดพลาดจริง แต่ถึงกระนั้นก็ไม่ควรปล่อยไว้


1

ตามที่มีคนกล่าวถึงในความคิดเห็นไปป์ keyvalue ไม่คงลำดับของการแทรก (ซึ่งเป็นจุดประสงค์หลักของแผนที่)

อย่างไรก็ตามดูเหมือนว่าหากคุณมีวัตถุแผนที่และต้องการรักษาคำสั่งไว้วิธีที่สะอาดที่สุดคือฟังก์ชัน entries ():

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

โปรดดูgithub.com/angular/angular/issues/31420#issuecomment-635377113เหตุผลที่คุณควรหลีกเลี่ยงการใช้ .entries ()
Florian

1

รหัสด้านล่างที่มีประโยชน์ที่จะแสดงในแผนที่เพื่อแทรก

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

.tsเพิ่มรหัสด้านล่าง

asIsOrder(a, b) {
    return 1;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.