如何使用包含键作为字符串和值作为映射迭代的 ngFor 循环 Map 进行迭代

我是新的角度5和尝试迭代地图包含另一个地图在打印脚本。 如何迭代下面这种地图的角度 下面是组件的代码:

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>

runtime error

179885 次浏览

这是因为 map.keys()返回一个迭代器。*ngFor可以使用迭代器,但是每个变化检测周期都会调用 map.keys(),从而产生对数组的新引用,导致您看到的错误。顺便说一下,这并不总是像你通常认为的那样是一个 错误; 它甚至可能不会破坏你的任何功能,但是它表明你有一个数据模型,它似乎以一种疯狂的方式运行——变化比变化检测器检查它的值更快。

如果不想将映射转换为组件中的数组,可以使用注释中建议的管道。似乎没有其他的解决办法。

附言。这个错误不会在生产模式中显示出来,因为它更像是一个非常严格的警告,而不是一个实际的错误,但是,这仍然不是一个好主意。

对于 Angular 6.1 + ,可以使用默认管道 < a href = “ https://angular.io/api/common/KeyValuePipe”rel = “ norefrer”> keyvalue ( 做复习和更新也 ) :

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

正在演示


对于前一个版本:

一个简单的解决方案是将 map 转换为 array: < a href = “ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global _ Objects/Array.from”rel = “ norefrer”> 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>

正在演示

剪辑

对于角度6.1和更新的,使用由 Londeren 建议的 KeyValuePipe

角度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 演示

如果您正在使用角度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']]);
}

正如人们在注释中提到的,keyvalue 管道不保留插入顺序(这是 Map 的主要用途)。

无论如何,如果您有一个 Map 对象并希望保持顺序,最简洁的方法是 entry ()函数:

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

角度的 keyvalue管可以使用,但不幸的是,它排序的关键。地图已经有一个订单,这将是伟大的,能够保持它!

我们可以定义自己的管道 mapkeyvalue,它保留了地图中项目的顺序:

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


// Holds a weak reference to its key (here a map), so if it is no longer referenced its value can be garbage collected.
const cache = new WeakMap<ReadonlyMap<any, any>, Array<{ key: any; value: any }>>();


@Pipe({ name: 'mapkeyvalue', pure: true })
export class MapKeyValuePipe implements PipeTransform {
transform<K, V>(input: ReadonlyMap<K, V>): Iterable<{ key: K; value: V }> {
const existing = cache.get(input);
if (existing !== undefined) {
return existing;
}


const iterable = Array.from(input, ([key, value]) => ({ key, value }));
cache.set(input, iterable);
return iterable;
}
}

它可以这样使用:

<mat-select>
<mat-option *ngFor="let choice of choicesMap | mapkeyvalue" [value]="choice.key">
\{\{ choice.value }}
</mat-option>
</mat-select>