Pregunta Iteramos sobre el objeto en Angular


Estoy tratando de hacer algunas cosas en Angular 2 Alpha 28, y estoy teniendo un problema con los diccionarios y NgFor.

Tengo una interfaz en TypeScript que se ve así:

interface Dictionary {
    [ index: string ]: string
}

En JavaScript, esto se traducirá en un objeto que con los datos podría tener este aspecto:

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

Quiero repetir esto e intenté esto:

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

Pero fue en vano, ninguno de los siguientes funcionó:

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

En todos los casos, recibo errores como "Token inesperado" o "No puedo encontrar el objeto de soporte de la tubería iterableDiff".

¿Que me estoy perdiendo aqui? ¿Esto ya no es posible? (La primera sintaxis funciona en Angular 1.x) o es la sintaxis diferente para iterar sobre un objeto?


75
2017-07-18 11:30


origen


Respuestas:


Parece que no quieren admitir la sintaxis de ng1.

De acuerdo con Miško Hevery (referencia)

Los mapas no tienen órdenes en las teclas y, por lo tanto, la iteración es impredecible.   Esto fue soportado en ng1, pero creemos que fue un error y no lo hará   ser compatible con NG2

El plan es tener una tubería mapToIterable

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

Entonces, para iterar sobre su objeto necesitará usar un "tubo". Actualmente no hay tubo implementado que hace eso.

Como solución, aquí hay un pequeño ejemplo que itera sobre las teclas:

Componente:

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



intenta usar esta tubería

@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



Actualización 2018:

Como las otras respuestas han mencionado, no es compatible con ngx, así que aquí hay una solución con pares clave-valor:

El tubo:

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

El uso:

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

Ejemplo de Stackblitz: https://stackblitz.com/edit/map-to-iterable-pipe


31
2018-02-08 01:14



Además de la respuesta de @ obscur, aquí hay un ejemplo de cómo puede acceder tanto a key y value de @View.

Tubo:

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

}

Ver:

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

Entonces, si el objeto se viera así:

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

El resultado generado sería:

Nombre: Daario
Apellido: Naharis

Nombre: Victarion
Apellido: Greyjoy

Nombre: Quentyn
Apellido: Ball


18
2018-03-03 18:14



Añadiendo a SimonHawesome's excelente respuesta. He hecho una versión sucinta que utiliza algunas de las nuevas características mecanografiadas. Me doy cuenta de que la versión de SimonHawesome es intencionalmente detallada para explicar los detalles subyacentes. También agregué una verificación temprana para que la tubería funcione falsy valores. Por ejemplo, si el mapa es null.

Tenga en cuenta que el uso de una transformación de iterador (como se hace aquí) puede ser más eficiente ya que no es necesario asignar memoria para una matriz temporal (como se hace en algunas de las otras respuestas).

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



Aquí hay una variación de algunas de las respuestas anteriores que admite transformaciones múltiples (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;
  }
}

Uso

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



Tuve un problema similar, construí algo para objetos y Maps.

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



Angular 2.x && Angular 4.x no son compatibles con esta versión de fábrica

Puede usar estas dos tuberías para iterar ya sea por llave o por valor.

Tubo de llaves:

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

Tubería de valores:

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])
  }
}

Cómo utilizar:

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



Si alguien se pregunta cómo trabajar con objetos multidimensionales, aquí está la solución.

supongamos que tenemos el siguiente objeto en Servicio

getChallenges() {
    var objects = {};
    objects['0'] = { 
        title: 'Angular2', 
        description : "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
    };

    objects['1'] = { 
        title: 'AngularJS', 
        description : "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
    };

    objects['2'] = { 
        title: 'Bootstrap',
        description : "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
    };
    return objects;
}

en componente agregar siguiente función

challenges;

constructor(testService : TestService){
    this.challenges = testService.getChallenges();
}
keys() : Array<string> {
    return Object.keys(this.challenges);
}

finalmente a la vista hacer lo siguiente

<div *ngFor="#key of keys();">
    <h4 class="heading">{{challenges[key].title}}</h4>
    <p class="description">{{challenges[key].description}}</p>
</div>

2
2018-01-08 20:32



Me he estado arrancando los pelos al tratar de analizar y usar los datos devueltos desde una llamada JSON query / api. No estoy seguro de exactamente dónde estaba yendo mal, siento que he estado dando vueltas a la respuesta por días, persiguiendo varios códigos de error como:

"No se puede encontrar el objeto de soporte de tubería 'iterableDiff'"

"Genérico TYpe Array requiere un argumento (s)"

JSON analizar errores, y estoy seguro de que otros

Supongo que acabo de tener la combinación equivocada de arreglos.

Así que aquí hay un pequeño resumen de los errores y cosas que hay que buscar.

Primero, verifique el resultado de sus llamadas de API, sus resultados pueden ser en forma de un objeto, una matriz o una matriz de objetos.

No entraré demasiado, basta con decir que el error original de OP de no ser iterable generalmente se debe a que intenta iterar un objeto, no una matriz.

Aquí están algunos de mis resultados de depuración que muestran las variables de ambas matrices y objetos

Entonces, como generalmente deseamos iterar sobre nuestro resultado JSON, debemos asegurarnos de que sea en forma de una matriz. Probé numerosos ejemplos, y tal vez sabiendo lo que sé ahora algunos de ellos funcionarían, pero el enfoque con el que fui fue para implementar un tubo y el código que utilicé fue el publicado por t.888.

   transform(obj: {[key: string]: any}, arg: string) {
if (!obj)
        return undefined;

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;

Honestamente creo que una de las cosas que me estaba causando era la falta de manejo de errores, al agregar la llamada 'return undefined' creo que ahora estamos permitiendo que se envíen datos no esperados a la tubería, lo que obviamente estaba ocurriendo en mi caso .

Si no quiere lidiar con el argumento de la tubería (y mire que no creo que sea necesario en la mayoría de los casos) puede devolver lo siguiente

       if (!obj)
          return undefined;
       return Object.keys(obj);

Algunas notas sobre la creación de su tubería y página o componente que utiliza esa tubería

¿Estaba recibiendo errores sobre 'name_of_my_pipe' que no se encuentra?

Use el comando 'ionic generate pipe' de la CLI para asegurarse de que los modules.ts de tubería se creen y se mencionen correctamente. asegúrese de agregar lo siguiente a la página mypage.module.ts.

import { PipesModule } from ‘…/…/pipes/pipes.module’;

(no estoy seguro de si esto cambia si también tienes tu propio custom_module, también puedes necesitar agregarlo al custommodule.module.ts)

si usó el comando 'generar iónico' para hacer su página, pero decide usar esa página como página principal, recuerde eliminar la referencia de página de app.module.ts (esta es otra respuesta que publiqué sobre ese tema). https://forum.ionicframework.com/t/solved-pipe-not-found-in-custom-component/95179/13?u=dreaser

En mi búsqueda de respuestas, encontré varias maneras de mostrar los datos en el archivo html, y no entiendo lo suficiente como para explicar las diferencias. Puede que le resulte mejor usar uno sobre otro en ciertos escenarios.

            <ion-item *ngFor="let myPost of posts">
                  <img src="https://somwhereOnTheInternet/{{myPost.ImageUrl}}"/>
                  <img src="https://somwhereOnTheInternet/{{posts[myPost].ImageUrl}}"/>
                  <img [src]="'https://somwhereOnTheInternet/' + myPost.ImageUrl" />
            </ion-item>

Sin embargo, lo que funcionó que me permitió mostrar el valor y la clave fue la siguiente:

    <ion-list>  
      <ion-item *ngFor="let myPost of posts  | name_of_pip:'optional_Str_Varible'">

        <h2>Key Value = {{posts[myPost]}} 

        <h2>Key Name = {{myPost}} </h2>

      </ion-item>
   </ion-list>  

para realizar la llamada API, parece que necesita importar HttpModule a app.module.ts

import { HttpModule } from '@angular/http';
 .
 .  
 imports: [
BrowserModule,
HttpModule,

y necesitas Http en la página desde la que realizas la llamada

import {Http} from '@angular/http';

Al hacer la llamada a la API, pareces poder acceder a los datos de los niños (los objetos o matrices dentro de la matriz) de dos maneras diferentes, o bien parecen funcionar

ya sea durante la llamada

this.http.get('https://SomeWebsiteWithAPI').map(res => res.json().anyChildren.OrSubChildren).subscribe(
        myData => {

o cuando asigna los datos a su variable local

posts: Array<String>;    
this.posts = myData['anyChildren'];

(no estoy seguro de si esa variable necesita ser una Cadena de Matriz, pero eso es lo que tengo ahora. Puede funcionar como una variable más genérica)

Y nota final, no era necesario usar la biblioteca JSON incorporada sin embargo, puede encontrar estas 2 llamadas útiles para convertir de un objeto a una cadena y viceversa

        var stringifiedData = JSON.stringify(this.movies);                  
        console.log("**mResults in Stringify");
        console.log(stringifiedData);

        var mResults = JSON.parse(<string>stringifiedData);
        console.log("**mResults in a JSON");
        console.log(mResults);

Espero que esta compilación de información ayude a alguien.


2
2018-01-07 07:24



Hasta ahora, la mejor / más corta respuesta que encontré es (sin ningún filtro de tubería o función personalizada desde el lado del componente)

Lado del componente:

objectKeys = Object.keys;

Lado de la plantilla:

<div *ngFor='let key of objectKeys(jsonObj)'>
   Key: {{key}}

    <div *ngFor='let obj of jsonObj[key]'>
        {{ obj.title }}
        {{ obj.desc }}
    </div>

</div>

DEMO DE TRABAJO


2
2018-02-09 06:26