Pytanie Iteruj nad obiektem w Angular


Próbuję zrobić kilka rzeczy w Angular 2 Alpha 28 i mam problem ze słownikami i NgFor.

Mam interfejs w TypeScript w następujący sposób:

interface Dictionary {
    [ index: string ]: string
}

W JavaScript będzie to tłumaczyć na obiekt, który z danymi może wyglądać tak:

myDict={'key1':'value1','key2':'value2'}

Chcę iterować nad tym i próbowałem to:

<div *ngFor="(#key, #value) of myDict">{{key}}:{{value}}</div>

Ale bezskutecznie, żaden z poniższych nie działał:

<div *ngFor="#value of myDict">{{value}}</div>
<div *ngFor="#value of myDict #key=index">{{key}}:{{value}}</div>

We wszystkich przypadkach pojawiają się błędy typu "Obiekt nieoczekiwanego tokena" lub "Nie można znaleźć" iterableDiff "obiektu obsługującego potoki"

Czego tu mi brakuje? Czy to już nie jest możliwe? (Pierwsza składnia działa w Angular 1.x) lub jest inna składnia dla iteracji nad obiektem?


76
2017-07-18 11:30


pochodzenie


Czym jest "słownik"? Nigdy nie widziałem ani nie słyszałem tego terminu w kontekście JavaScript, Angular lub TypeScript. Y


Odpowiedzi:


Wygląda na to, że nie chcą obsługiwać składni z ng1.

Według Miško Hevery (odniesienie):

Mapy nie mają zamówień na klucze, dlatego ich iteracja jest nieprzewidywalna.   Było to obsługiwane w ng1, ale uważamy, że to był błąd i nie będzie   być wspierane w NG2

Plan zakłada posiadanie fajki mapowej

<div *ngFor"var item of map | mapToIterable">

Aby wykonać iterację nad obiektem, musisz użyć "rury". Obecnie nie ma rura wdrożone, które to robi.

Aby obejść ten problem, oto mały przykład, który polega na iteracji kluczy:

Składnik:

import {Component} from 'angular2/core';

@Component({
  selector: 'component',
  templateUrl: `
       <ul>
       <li *ngFor="#key of keys();">{{key}}:{{myDict[key]}}</li>
       </ul>
  `
})
export class Home {
  myDict : Dictionary;
  constructor() {
    this.myDict = {'key1':'value1','key2':'value2'};
  }

  keys() : Array<string> {
    return Object.keys(this.myDict);
  }
}

interface Dictionary {
    [ index: string ]: string
}

68
2017-07-21 11:22



Próbuję tego samego na obiekcie key tak jak number i value tak jak string ale kątowa to błąd rzucania expression has changed after it was checked ? dlaczego tak jest jakikolwiek pomysł? - Pardeep Jain
Tak, to też się dzieje. I to samo, jeśli używam również rozwiązania @ obsur. - user2294382


spróbuj użyć tej rury

@Pipe({ name: 'values',  pure: false })
export class ValuesPipe implements PipeTransform {
  transform(value: any, args: any[] = null): any {
    return Object.keys(value).map(key => value[key]);
  }
}

<div *ngFor="#value of object | values"> </div>

60
2017-12-03 19:26



Świetne, a jeśli chcę zachować odniesienie do klucza, po prostu zamapuję obiekt z kluczem i wartością. Chciałbym zaznaczyć kilka odpowiedzi jako zaakceptowaną odpowiedź, ponieważ jest to rozwiązanie mojego problemu, podczas gdy zaznaczona odpowiedź jest odpowiedzią na moje pytanie. - Rickard Staaf
@obscur - jeśli wykonam powyższe czynności, otrzymuję komunikat "wyrażenie zmieniło się po sprawdzeniu" przy użyciu parametru angle2.beta.0.0. jakieś pomysły? - user2294382
To dlatego, że czyste: fałsz wymaga wykrywania zmian. Strategia do wstrzyknięcia - Judson Terrell
Dlaczego ustawienie to jest nieczyste? - tom10271


Aktualizacja 2018:

Jak wspomniały inne odpowiedzi, nie jest to obsługiwane w Ngx, więc oto sposób obejścia tego problemu pary klucz-wartość:

Rura:

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
  name: 'mapToIterable'
})
export class MapToIterable implements PipeTransform {
  transform(dict: Object) {
    var a = [];
    for (var key in dict) {
      if (dict.hasOwnProperty(key)) {
        a.push({key: key, val: dict[key]});
      }
    }
    return a;
  }
}

Użycie:

<div *ngFor="let keyValuePair of someObject | mapToIterable">
  This is the key {{keyValuePair.key}} and this is the value {{keyValuePair.val}}.
</div>

Przykład Stackblitz: https://stackblitz.com/edit/map-to-iterable-pipe


33
2018-02-08 01:14



Powinieneś dodać implements PipeTransform w definicji klasy (zob angular.io/guide/pipes#custom-pipe) - toioski
@toioski dzięki, dodałem go i zaktualizowałem do nowej składni pętli for. - bersling
Świetna odpowiedź, użyła tego do ngDo mojego Słownika. Musiałem zrobić keyValuePair.val [0], chociaż moje wartości zakończyły się [{}], a nie {} - jhhoff02
dzięki. Działa świetnie - Johansrk


Oprócz odpowiedzi @ obscur, tutaj jest przykład, w jaki sposób można uzyskać dostęp do obu key i value z @View.

Rura:

@Pipe({
   name: 'keyValueFilter'
})

export class keyValueFilterPipe {
    transform(value: any, args: any[] = null): any {

        return Object.keys(value).map(function(key) {
            let pair = {};
            let k = 'key';
            let v = 'value'


            pair[k] = key;
            pair[v] = value[key];

            return pair;
        });
    }

}

Widok:

<li *ngFor="#u of myObject | 
keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>

Jeśli obiekt miałby wyglądać tak:

myObject = {
    Daario: Naharis,
    Victarion: Greyjoy,
    Quentyn: Ball
}

Wygenerowany wynik będzie następujący:

Imię: Daario
Nazwisko: Naharis

Imię: Victarion
Nazwisko: Greyjoy

Imię: Quentyn
Nazwisko: Ball


18
2018-03-03 18:14



Pracował dla mnie dość łatwo! Dzięki - Vlad
wystarczy wspomnieć o zmianie widoku: as <li *ngFor="let u of myObject | keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>. +1 ode mnie. - sib10
Niesamowite ! Dziękuję Ci. - Maxime Lafarie


Dodanie do SimonHawesome's doskonała odpowiedź. Zrobiłem zwięzłą wersję, która wykorzystuje niektóre z nowych funkcji maszynopisu. Zdaję sobie sprawę, że wersja SimonHawesome jest celowo dokładna, aby wyjaśnić podstawowe szczegóły. Dodałem również test wczesnego wyjazdu, aby rura działała falsy wartości. Np. Jeśli mapa jest null.

Zauważ, że użycie transformacji iteracyjnej (jak to zrobiono tutaj) może być bardziej wydajne, ponieważ nie musimy przydzielać pamięci dla tymczasowej tablicy (jak to zrobiono w niektórych innych odpowiedziach).

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
    name: 'mapToIterable'
})
export class MapToIterable implements PipeTransform {
    transform(map: { [key: string]: any }, ...parameters: any[]) {
        if (!map)
            return undefined;
        return Object.keys(map)
            .map((key) => ({ 'key': key, 'value': map[key] }));
    }
}

12
2018-05-27 09:15



Uwielbiam ten wątek, z jednym komentarzem budującym ontop innego! Już miałem napisać to samo, kiedy zobaczyłem twój kod - David
jedyna rzecz w tym rozwiązaniu: powinna zaimplementować PipeTransform - iRaS
@iRaS Dobra uwaga. Zaktualizowałem swoją odpowiedź. Również zwracam undefined zamiast null. - Frederik Aalund


Oto odmiany niektórych z powyższych odpowiedzi, które obsługują wiele transformacji (keyval, key, value):

import { Pipe, PipeTransform } from '@angular/core';

type Args = 'keyval'|'key'|'value';

@Pipe({
  name: 'mapToIterable',
  pure: false
})
export class MapToIterablePipe implements PipeTransform {
  transform(obj: {}, arg: Args = 'keyval') {
    return arg === 'keyval' ?
        Object.keys(obj).map(key => ({key: key, value: obj[key]})) :
      arg === 'key' ?
        Object.keys(obj) :
      arg === 'value' ?
        Object.keys(obj).map(key => obj[key]) :
      null;
  }
}

Stosowanie

map = {
    'a': 'aee',
    'b': 'bee',
    'c': 'see'
}

<div *ngFor="let o of map | mapToIterable">{{o.key}}: {{o.value}}</div>
  <div>a: aee</div>
  <div>b: bee</div>
  <div>c: see</div>

<div *ngFor="let o of map | mapToIterable:'keyval'">{{o.key}}: {{o.value}}</div>
  <div>a: aee</div>
  <div>b: bee</div>
  <div>c: see</div>

<div *ngFor="let k of map | mapToIterable:'key'">{{k}}</div>
  <div>a</div>
  <div>b</div>
  <div>c</div>

<div *ngFor="let v of map | mapToIterable:'value'">{{v}}</div>
  <div>aee</div>
  <div>bee</div>
  <div>see</div>

9
2018-01-11 22:32



pure: false jest bardzo ważne dla błyskawicznych odbić. - Fırat KÜÇÜK


Miałem podobny problem, zbudowałem coś dla obiektów i Map.

import { Pipe } from 'angular2/core.js';

/**
 * Map to Iteratble Pipe
 * 
 * It accepts Objects and [Maps](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)
 * 
 * Example:
 * 
 *  <div *ngFor="#keyValuePair of someObject | mapToIterable">
 *    key {{keyValuePair.key}} and value {{keyValuePair.value}}
 *  </div>
 * 
 */
@Pipe({ name: 'mapToIterable' })
export class MapToIterable {
  transform(value) {
    let result = [];
    
    if(value.entries) {
      for (var [key, value] of value.entries()) {
        result.push({ key, value });
      }
    } else {
      for(let key in value) {
        result.push({ key, value: value[key] });
      }
    }

    return result;
  }
}


4
2018-04-13 23:27



Działa to dobrze, z wyjątkiem tego, że w TypeScript powinieneś dodać implements PipeTransform do definicji klasy - GeorgDangl


Angular 2.x && Angular 4.x nie obsługuje tego po wyjęciu z pudełka

Możesz użyć tych dwóch potoków do iterowania według klawisz lub przez wartość.

Rura klucza:

import {Pipe, PipeTransform} from '@angular/core'

@Pipe({
  name: 'keys',
  pure: false
})
export class KeysPipe implements PipeTransform {
  transform(value: any, args: any[] = null): any {
    return Object.keys(value)
  }
}

Rurka wartości:

import {Pipe, PipeTransform} from '@angular/core'

@Pipe({
  name: 'values',
  pure: false
})
export class ValuesPipe implements PipeTransform {
  transform(value: any, args: any[] = null): any {
    return Object.keys(value).map(key => value[key])
  }
}

Jak używać:

let data = {key1: 'value1', key2: 'value2'}

<div *ngFor="let key of data | keys"></div>
<div *ngFor="let value of data | values"></div>

3
2018-05-30 06:15