【问题标题】:How to iterate using ngFor loop Map containing key as string and values as map iteration如何使用包含键作为字符串和值作为映射迭代的ngFor循环映射进行迭代
【发布时间】: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>

【问题讨论】:

标签: javascript angular typescript angular-cli


【解决方案1】:

这是因为map.keys() 返回一个迭代器。 *ngFor 可以与迭代器一起使用,但 map.keys() 将在每个更改检测循环中被调用,从而产生对数组的新引用,从而导致您看到的错误。顺便说一句,这并不总是像您传统上所认为的那样错误;它甚至可能不会破坏您的任何功能,但表明您的数据模型似乎以一种疯狂的方式表现 - 更改速度比更改检测器检查其值的速度快。

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

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

【讨论】:

    【解决方案2】:

    对于 Angular 6.1+,您可以使用默认管道 keyvalueDo 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 Map>();它是如何工作的?
    • @FerozSiddiqui,我已经更新了答案,我认为这适用于任何嵌套级别。
    • 如果我们最终将其转换为数组,我们将丢失所有地图特征。那么 Map 有什么好处呢?
    • @pro.mean ,我们正在转换只是因为我们想通过角度模板循环它,因为它需要一些可迭代的迭代。并且您仍然有原始对象 map 可用,您可以使用该对象来使用 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>
    

    【讨论】:

    【解决方案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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-14
      • 1970-01-01
      • 2011-10-04
      • 1970-01-01
      • 2021-10-13
      • 2021-07-05
      • 1970-01-01
      相关资源
      最近更新 更多