How to iterate using ngFor loop Map containing key as string and values as map iteration

I am new to angular 5 and trying to iterate the map containing another map in typescript. How to iterate below this kind of map in angular below is code for component:

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

  }



}

and its template html is :

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


</ul>

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

runtime error


For Angular 6.1+ , you can use default pipe keyvalue ( Do review and upvote also ) :

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

WORKING DEMO


For the previous version :

One simple solution to this is convert map to array : Array.from

Component Side :

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

Template Side :

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

WORKING DEMO


If you are using Angular 6.1 or later, the most convenient way is to use 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']]);
    }

Edit

For angular 6.1 and newer, use the KeyValuePipe as suggested by Londeren.

For angular 6.0 and older

To make things easier, you can create a pipe.

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

As it it pure, it will not be triggered on every change detection, but only if the reference to the map variable changes

Stackblitz demo


The below code useful to display in the map insertion order.

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

In .ts file add the below code.

asIsOrder(a, b) {
    return 1;
}

This is because map.keys() returns an iterator. *ngFor can work with iterators, but the map.keys() will be called on every change detection cycle, thus producing a new reference to the array, resulting in the error you see. By the way, this is not always an error as you would traditionally think of it; it may even not break any of your functionality, but suggests that you have a data model which seems to behave in an insane way - changing faster than the change detector checks its value.

If you do no want to convert the map to an array in your component, you may use the pipe suggested in the comments. There is no other workaround, as it seems.

P.S. This error will not be shown in the production mode, as it is more like a very strict warning, rather than an actual error, but still, this is not a good idea to leave it be.