Як виконати ітерацію за допомогою карти циклу ngFor, що містить ключ як рядок та значення як ітерацію карти


109

Я новачок у кутовому 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>

помилка під час виконання



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


подяка працює дуже добре
Фероз Сіддікі

Відповіді:


246

Для 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, я оновив відповідь, і я думаю, що це буде працювати на будь-якому вкладеному рівні.
Вівек Доші

ми втратили всю функцію Map, якщо остаточно перетворимо її на Array. у чому тоді перевага Карти?
diEcho

1
@ pro.mean, ми перетворюємо лише тому, що хочемо прокрутити його через кутовий шаблон, оскільки він хоче, щоб якийсь ітерабель повторювався. і у вас все ще є оригінальний об’єкт, mapви можете використовувати цей, щоб використати всі переваги карти
Vivek Doshi

1
@ pro.mean, ми конвертували лише для того, щоб показати це на стороні шаблону, але у вас все ще є Map obaject, який ви можете використовувати на стороні вашого компонента. ти можеш попросити ОП why he needed this kind of requirement, він може тобі краще пояснити :)
Вівек Доші

48

Якщо ви використовуєте 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
Лондерен

24

Редагувати

Для кутових 6.1 та новіших версій використовуйте 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


Я думаю, що ваш канал буде перевірятися на наявність змін і робити його повну ітерацію на кожному циклі виявлення змін, оскільки він не є "чистим". Не голосували, але можливо тому?
Дренай

5
@Ryan Pipes є типовими за замовчуванням. Якщо ви додасте console.log в конвеєр, ви побачите, що він не отримує виклик кілька разів. Але метод компонентів у прийнятому рішенні робить ...
Девід,

1
Я отримав це назад! Дякую за голови
Дренай

10

Це тому, що map.keys()повертає ітератор. *ngForможе працювати з ітераторами, але map.keys()буде викликатись при кожному циклі виявлення змін, створюючи таким чином нове посилання на масив, що призводить до помилки, яку ви бачите. До речі, це не завжди помилка, як ви традиційно думаєте про це; це може навіть не порушити жодної вашої функціональності, але припускає, що у вас є модель даних, яка, здається, поводиться несамовито - змінюється швидше, ніж детектор змін перевіряє її значення.

Якщо ви не хочете перетворювати карту на масив у своєму компоненті, ви можете використовувати конвеєр, запропонований у коментарях. Інших обхідних шляхів, як здається, немає.

PS Ця помилка не буде відображатися у виробничому режимі, оскільки вона швидше нагадує дуже суворе попередження, а не фактичну помилку, але все ж це не гарна ідея залишати це.


1

Як люди вже згадували в коментарях keyvalue pipe не зберігає порядок вставки (що є основною метою Map).

У будь-якому випадку, схоже, якщо у вас є об’єкт Map і ви хочете зберегти порядок, найпростіший спосіб це зробити - це функція entries ():

<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 ()
Флоріан

1

Наведений нижче код корисний для відображення в порядку вставки карти .

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

.ts додайте код нижче.

asIsOrder(a, b) {
    return 1;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.