Как выполнить итерацию с использованием карты цикла ngFor, содержащей ключ в виде строки и значения в виде итерации карты

104

Я новичок в angular 5 и пытаюсь перебрать карту, содержащую другую карту, в машинописном тексте. Как выполнить итерацию ниже этого вида карты в angular, ниже приведен код для компонента:

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>

ошибка выполнения

Фероз Сиддики
источник
какое решение я не хочу преобразовывать свою карту в массив
Фероз Сиддики
спасибо работает очень хорошо
Фероз Сиддики

Ответы:

232

Для 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 >> (); как это работает?
Фероз Сиддики
@FerozSiddiqui, я обновил ответ и думаю, что он будет работать для любого вложенного уровня.
Вивек Доши
мы потеряли все возможности карты, если наконец преобразовали ее в массив. в чем тогда преимущество карты?
diEcho 05
1
@ pro.mean, мы конвертируем только потому, что хотим перебрать его через шаблон angular, так как он хочет, чтобы какой-то итеративный процесс повторялся. и у вас все еще есть mapдоступный оригинальный объект, вы можете использовать его, чтобы использовать все преимущества Карты
Вивек Доши
1
@ pro.mean, мы преобразовали только для того, чтобы показать это на стороне шаблона, но у вас все еще есть объект Map, который вы можете использовать на стороне компонента. вы можете попросить OP why he needed this kind of requirement, он вам лучше объяснит :)
Vivek Doshi
46

Если вы используете 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
Надеется , люди прокрутка и посмотреть этот ответ, потому что я собирался сделать рефакторинг , пока я не видел это вне коробки трубы для моего mapтипа , который заботится о его использовании с*ngFor
atconway
2
похоже, keyvalue не сохраняет первоначальную сортировку карты :-(, вам нужно добавить функцию сравнения, чтобы исправить это
Константин Вахрушев
1
Это верно. И вот проблема github.com/angular/angular/issues/31420
Londeren
24

редактировать

Для angular 6.1 и новее используйте KeyValuePipe, как предлагает Londeren.

Для 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

Дэвид
источник
Я думаю, что ваш канал будет проверяться на наличие изменений и выполнять полную итерацию в каждом цикле обнаружения изменений, так как он не «чистый». Не голосовал против, но, может быть, поэтому?
Дренай
4
@Ryan Pipes по умолчанию чистые. Если вы добавите console.log в канал, вы увидите, что он не вызывается несколько раз. Но компонентный метод в принятом решении делает ...
Дэвид
1
Я получил это задом наперед! Спасибо за хедз-ап
Дренай
10

Это потому, что map.keys()возвращает итератор. *ngForможет работать с итераторами, но map.keys()будет вызываться в каждом цикле обнаружения изменений, создавая новую ссылку на массив, что приведет к ошибке, которую вы видите. Кстати, это не всегда ошибка, как вы традиционно думаете; он может даже не нарушить вашу функциональность, но предполагает, что у вас есть модель данных, которая, кажется, ведет себя безумно - изменяется быстрее, чем детектор изменений проверяет свое значение.

Если вы не хотите преобразовывать карту в массив в вашем компоненте, вы можете использовать конвейер, предложенный в комментариях. Похоже, другого обходного пути нет.

PS Эта ошибка не будет отображаться в производственном режиме, поскольку она больше похожа на очень строгое предупреждение, чем на фактическую ошибку, но, тем не менее, оставлять ее - не лучшая идея.

Армен Варданян
источник
1

Как люди упоминали в комментариях, канал ключевого значения не сохраняет порядок вставки (что является основной целью карты).

Во всяком случае, похоже, что если у вас есть объект Map и вы хотите сохранить порядок, самый чистый способ сделать это - это функция entry ():

<ul>
    <li *ngFor="let item of map.entries()">
        <span>key: {{item[0]}}</span>
        <span>value: {{item[1]}}</span>
    </li>
</ul>
Wildhammer
источник
Please see github.com/angular/angular/issues/31420#issuecomment-635377113 why you should avoid using .entries()
Florian
1

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>

.ts file add the below code.

asIsOrder(a, b) {
    return 1;
}
Shakthifuture
источник