如何使用包含键作为字符串和值作为映射迭代的ngFor循环映射进行迭代
Posted
技术标签:
【中文标题】如何使用包含键作为字符串和值作为映射迭代的ngFor循环映射进行迭代【英文标题】:How to iterate using ngFor loop Map containing key as string and values as map iteration 【发布时间】:2018-06-19 14:52:34 【问题描述】:我是 Angular 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>
【问题讨论】:
github.com/angular/angular/issues/2246 我不想将地图转换为数组的解决方案是什么 stackblitz.com/edit/angular-ifebvj?file=app%2Fapp.module.ts 谢谢,效果很好 stackblitz.com/edit/angular-jm8jgz?file=app/app.component.ts 【参考方案1】:这是因为map.keys()
返回一个迭代器。 *ngFor
可以与迭代器一起使用,但 map.keys()
将在每个更改检测循环中被调用,从而产生对数组的新引用,从而导致您看到的错误。顺便说一句,这并不总是像您传统上所认为的那样错误;它甚至可能不会破坏您的任何功能,但表明您的数据模型似乎以一种疯狂的方式表现 - 更改速度比更改检测器检查其值的速度快。
如果您不想在组件中将映射转换为数组,则可以使用 cmets 中建议的管道。似乎没有其他解决方法。
附:此错误不会在生产模式中显示,因为它更像是一个非常严格的警告,而不是实际的错误,但仍然不是一个好主意。
【讨论】:
【参考方案2】:对于 Angular 6.1+,您可以使用默认管道 keyvalue
(Do review and upvote also):
<ul>
<li *ngFor="let recipient of map | keyvalue">
recipient.key --> recipient.value
</li>
</ul>
WORKING DEMO
对于以前的版本:
一个简单的解决方案是将映射转换为数组: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>
WORKING DEMO
【讨论】:
if map = new Mapmap
可用,您可以使用该对象来使用 Map 的所有好处
@pro.mean ,我们已经转换只是为了在模板端显示这一点,但您仍然可以在组件端使用 Map 对象。您可以向 OP why he needed this kind of requirement
提问,他可以更好地为您解释 :)【参考方案3】:
编辑
对于 Angular 6.1 和更新版本,请按照 Londeren 的建议使用 KeyValuePipe。
对于 Angular 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 demo
【讨论】:
我认为您的管道将被检查更改并在每个更改检测周期进行完整迭代,因为它不是“纯”的。没有投反对票,但也许这就是原因? @Ryan Pipes 默认是纯的。如果您在管道中添加一个 console.log,您会看到它不会被多次调用。但是接受的解决方案中的组件方法确实...... 我倒退了!感谢您的提醒【参考方案4】:如果你使用的是 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']]);
【讨论】:
希望人们滚动并看到这个答案,因为我正要进行重构,直到我看到这个开箱即用的管道可用于我的map
类型,它负责使用它与*ngFor
看来,keyvalue并没有保持map的初始排序:-(,你需要添加比较器功能来解决这个问题
这是真的。这是一个问题github.com/angular/angular/issues/31420
哇!我不知道有一个管道可以如此轻松地迭代模板中的地图。这很有帮助。谢谢!【参考方案5】:
正如人们在 cmets 中提到的 keyvalue 管道不保留插入顺序(这是 Map 的主要目的)。
无论如何,如果你有一个 Map 对象并且想要保留顺序,最简洁的方法是 entry() 函数:
<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 的评论,这为我节省了数小时的调试时间 不要使用它 - 你会得到ExpressionChangedAfterItHasBeenCheckedError
【参考方案6】:
以下代码可用于在地图插入顺序中显示。
<ul>
<li *ngFor="let recipient of map | keyvalue: asIsOrder">
recipient.key --> recipient.value
</li>
</ul>
在 .ts 文件中添加以下代码。
asIsOrder(a, b)
return 1;
【讨论】:
重要的是asIsOrder
函数。【参考方案7】:
Angular 的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>
【讨论】:
以上是关于如何使用包含键作为字符串和值作为映射迭代的ngFor循环映射进行迭代的主要内容,如果未能解决你的问题,请参考以下文章