page - Iterar sobre objeto en angular
tablas dinamicas angular 4 (17)
En JavaScript, esto se traducirá en un objeto que con datos podría verse así
Las interfaces en TypeScript son una construcción de tiempo de desarrollo (solo para herramientas ... 0 impacto en tiempo de ejecución). Debe escribir el mismo TypeScript que su JavaScript.
Estoy tratando de hacer algunas cosas en Angular 2 Alpha 28, y tengo 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 datos podría verse así:
myDict={''key1'':''value1'',''key2'':''value2''}
Quiero repetir esto y probé esto:
<div *ngFor="(#key, #value) of myDict">{{key}}:{{value}}</div>
Pero en vano, ninguno de los siguientes funcionó tampoco:
<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 se puede encontrar el objeto de soporte de tubería ''iterableDiff''"
¿Que me estoy perdiendo aqui? ¿Ya no es posible? (La primera sintaxis funciona en Angular 1.x) o ¿es diferente la sintaxis para iterar sobre un objeto?
Angular 6.1.0+ Respuesta
Use el
keyvalue
incorporado
keyvalue
esta manera:
<div *ngFor="let item of myObject | keyvalue">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
o así:
<div *ngFor="let item of myObject | keyvalue:mySortingFunction">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
donde
mySortingFunction
está en su archivo
.ts
, por ejemplo:
mySortingFunction = (a, b) => {
return a.key > b.key ? -1 : 1;
}
Stackblitz: https://stackblitz.com/edit/angular-iterate-key-value
No necesitará registrar esto en ningún módulo, ya que las tuberías angulares funcionan de fábrica en cualquier plantilla.
También funciona para Javascript-Maps .
Pre-Angular 6 Respuesta
Como las otras respuestas han mencionado, no es compatible con ngx, así que aquí hay una solución alternativa 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
Además de la respuesta de @ obscur, aquí hay un ejemplo de cómo puede acceder tanto a la
key
como al
value
desde @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="let 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: Bola
Agregando a la
excelente respuesta de
SimonHawesome.
He creado una versión sucinta que utiliza algunas de las nuevas características de mecanografía.
Me doy cuenta de que la versión de SimonHawesome es intencionalmente detallada para explicar los detalles subyacentes.
También he agregado una verificación de salida anticipada para que la tubería funcione para valores
falsy
.
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 necesitamos asignar memoria para una matriz temporal (como se hizo 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] }));
}
}
Aquí hay una variación de algunas de las respuestas anteriores que admite múltiples transformaciones (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>
Así que iba a implementar mi propia función auxiliar, objLength (obj), que devuelve solo Object (obj) .keys.length. Pero luego, cuando lo estaba agregando a mi plantilla * ngIf, mi IDE sugirió objectKeys (). Lo intenté y funcionó. Siguiendo con su declaración, parece ser ofrecido por lib.es5.d.ts, ¡así que ahí lo tienes!
Así es como lo implementé (tengo un objeto personalizado que usa claves generadas del lado del servidor como índice para los archivos que he cargado):
<div *ngIf="fileList !== undefined && objectKeys(fileList).length > 0">
<h6>Attached Files</h6>
<table cellpadding="0" cellspacing="0">
<tr *ngFor="let file of fileList | keyvalue">
<td><a href="#">{{file.value[''fileName'']}}</a></td>
<td class="actions">
<a title="Delete File" (click)="deleteAFile(file.key);">
</a>
</td>
</tr>
</table>
</div>
Defina
MapValuesPipe
e implemente
PipeTransform
:
import {Pipe, PipeTransform} from ''@angular/core'';
@Pipe({name: ''mapValuesPipe''})
export class MapValuesPipe implements PipeTransform {
transform(value: any, args?: any[]): Object[] {
let mArray:
value.forEach((key, val) => {
mArray.push({
mKey: key,
mValue: val
});
});
return mArray;
}
}
Agregue su tubería en su módulo de tuberías. Esto es importante si necesita usar la misma tubería en más de un componente :
@NgModule({
imports: [
CommonModule
],
exports: [
...
MapValuesPipe
],
declarations: [..., MapValuesPipe, ...]
})
export class PipesAggrModule {}
Luego, simplemente use la tubería en su html con
*ngFor
:
<tr *ngFor="let attribute of mMap | mapValuesPipe">
Recuerde, deberá declarar su PipesModule en el componente donde desea usar la tubería:
@NgModule({
imports: [
CommonModule,
PipesAggrModule
],
...
}
export class MyModule {}
El diccionario es un objeto, no una matriz. Creo que ng-repeat requiere una matriz en Angular 2.
La solución más simple sería crear una tubería / filtro que convierta el objeto en una matriz sobre la marcha. Dicho esto, probablemente quieras usar una matriz como dice @basarat.
Me he estado arrancando el pelo al intentar analizar y usar los datos devueltos de una consulta JSON / llamada de API. No estoy seguro exactamente de dónde estaba yendo mal, siento que he estado circulando la respuesta durante días, persiguiendo varios códigos de error como:
"No se puede encontrar el objeto de soporte de tubería ''iterableDiff''"
"La matriz de tipos genérica requiere uno o más argumentos"
Errores de análisis JSON, y estoy seguro de que otros
Supongo que acabo de tener la combinación incorrecta de soluciones.
Así que aquí hay un resumen de las trampas y cosas que debe buscar.
Primero verifique el resultado de sus llamadas a la API, sus resultados pueden ser en forma de un objeto, una matriz o una matriz de objetos.
No voy a profundizar demasiado, basta con decir que el error original del OP de no ser iterable generalmente es causado por tratar de iterar un objeto, no una matriz.
He aquí algunos de mis resultados de depuración que muestran variables de matrices y objetos.
Entonces, como generalmente nos gustaría iterar sobre nuestro resultado JSON, debemos asegurarnos de que tenga la forma de una matriz. Probé numerosos ejemplos, y tal vez sabiendo lo que sé ahora, algunos de ellos funcionarían, pero el enfoque que utilicé fue implementar una tubería 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 atrapó fue la falta de manejo de errores, al agregar la llamada ''devolver indefinido'' 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 desea lidiar con el argumento de la tubería (y mira, 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 cómo crear su tubería y página o componente que usa esa tubería
es que estaba recibiendo errores sobre ''nombre_de_mi_pipe'' no encontrado
Use el comando ''iónico generar tubería'' de la CLI para asegurarse de que los módulos de tubería.ts se creen y hagan referencia 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 tiene su propio custom_module, es posible que también deba agregarlo a custommodule.module.ts)
si usó el comando ''generar página iónica'' para crear su página, pero decide usar esa página como su página principal, recuerde eliminar la referencia de la página de app.module.ts (aquí hay otra respuesta que publiqué sobre ese https://forum.ionicframework.com/t/solved-pipe-not-found-in-custom-component/95179/13?u=dreaser
En mi búsqueda de respuestas, hubo varias formas 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 tanto el valor como la clave fue lo 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 hacer que la llamada a la API parezca que necesita importar HttpModule en app.module.ts
import { HttpModule } from ''@angular/http'';
.
.
imports: [
BrowserModule,
HttpModule,
y necesita Http en la página desde la que realiza la llamada
import {Http} from ''@angular/http'';
Al hacer la llamada a la API, parece que puede acceder a los datos secundarios (los objetos o las matrices dentro de la matriz) de dos maneras diferentes, o parece 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 debe 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 recopilación de información ayude a alguien.
Parece que no quieren admitir la sintaxis de ng1.
Según Miško Hevery ( reference ):
Los mapas no tienen órdenes en las claves y, por lo tanto, su iteración es impredecible. Esto se admitió en ng1, pero creemos que fue un error y no se admitirá en NG2
El plan es tener una tubería mapToIterable
<div *ngFor"var item of map | mapToIterable">
Entonces, para iterar sobre su objeto, necesitará usar una "tubería". Actualmente no hay una pipe implementada que haga eso.
Como solución alternativa, aquí hay un pequeño ejemplo que itera sobre las claves:
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
}
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>
Si tiene
es6-shim
o su
tsconfig.json
target
es6
, puede usar ES6
Map
para hacerlo.
var myDict = new Map();
myDict.set(''key1'',''value1'');
myDict.set(''key2'',''value2'');
<div *ngFor="let keyVal of myDict.entries()">
key:{{keyVal[0]}}, val:{{keyVal[1]}}
</div>
Tuve un problema similar, construí algo para objetos y mapas.
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;
}
}
intenta usar esta pipa
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]);
}
}
<div *ngFor="#value of object | values"> </div>
Actualizado: Angular ahora proporciona la tubería para recorrer el objeto json a través del valor
keyvalue
:
<div *ngFor="let item of myDict | keyvalue">
{{item.key}}:{{item.value}}
</div>
DEMO DE TRABAJO , y para más detalles Leer
Anteriormente (para la versión anterior): hasta ahora la respuesta mejor / más corta que encontré es (sin ningún filtro de tubería o función personalizada desde el lado del componente)
Lado 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>
Angular 2.x && Angular 4.x no admite esto fuera de la caja
Puede usar estos dos tubos para iterar, ya sea por clave 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>
//Get solution for ng-repeat
//Add variable and assign with Object.key
export class TestComponent implements OnInit{
objectKeys = Object.keys;
obj: object = {
"test": "value"
"test1": "value1"
}
}
//HTML
<div *ngFor="let key of objectKeys(obj)">
<div>
<div class="content">{{key}}</div>
<div class="content">{{obj[key]}}</div>
</div>