Итерация над объектом в Angular
Я пытаюсь сделать что-то в Angular 2 Alpha 28, и у меня проблема со словарями и NgFor.
У меня есть интерфейс в TypeScript, выглядящий так:
interface Dictionary {
[ index: string ]: string
}
В JavaScript это переведет объект, который с данными может выглядеть так:
myDict={'key1':'value1','key2':'value2'}
Я хочу перебрать это и пробовал это:
<div *ngFor="(#key, #value) of myDict">{{key}}:{{value}}</div>
Но безрезультатно ни один из ниже не работал:
<div *ngFor="#value of myDict">{{value}}</div>
<div *ngFor="#value of myDict #key=index">{{key}}:{{value}}</div>
Во всех случаях я получаю такие ошибки, как "Неожиданный токен" или "Не удается найти" поддерживающий объект "iterableDiff"
Что мне здесь не хватает? Разве это невозможно? (Первый синтаксис работает в Angular 1.x) или является синтаксисом, отличающимся для итерации над объектом?
Ответы
Ответ 1
Похоже, что они не хотят поддерживать синтаксис от ng1.
Согласно Miško Hevery (ссылка):
Карты не имеют заказов в ключах и, следовательно, итерация непредсказуема. Это было поддержано в ng1, но мы считаем, что это была ошибка и не будет поддерживаться в NG2
План состоит в том, чтобы иметь mapToIterable pipe
<div *ngFor"var item of map | mapToIterable">
Итак, чтобы перебрать ваш объект, вам нужно будет использовать "трубу".
В настоящее время не реализовано pipe, которое делает это.
Как обходной путь, вот небольшой пример, который выполняет итерации по клавишам:
компонент:
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
}
Ответ 2
Угловой 6.1. 0+ Ответ
Используйте встроенное значение keyvalue
-pipe следующим образом:
<div *ngFor="let item of myObject | keyvalue">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
или вот так:
<div *ngFor="let item of myObject | keyvalue:mySortingFunction">
Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>
где mySortingFunction
находится в вашем файле .ts
, например:
mySortingFunction = (a, b) => {
return a.key > b.key ? -1 : 1;
}
Stackblitz: https://stackblitz.com/edit/angular-iterate-key-value
Вам не нужно регистрировать это в любом модуле, так как угловые трубы работают из коробки в любом шаблоне.
Это также работает для Javascript-Maps.
Преангулярный 6 Ответ
Как уже упоминалось в других ответах, он не поддерживается в ngx, поэтому здесь есть обходной путь с парами ключ-значение:
Труба:
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;
}
}
Использование:
<div *ngFor="let keyValuePair of someObject | mapToIterable">
This is the key {{keyValuePair.key}} and this is the value {{keyValuePair.val}}.
</div>
Пример Stackblitz: https://stackblitz.com/edit/map-to-iterable-pipe
Ответ 3
попробуйте использовать эту трубу
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>
Ответ 4
В дополнение к ответу @obscur, вот пример того, как вы можете получить доступ к key
и value
из @View.
Труба:
@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;
});
}
}
Посмотреть:
<li *ngFor="let u of myObject |
keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>
Так что, если объект будет выглядеть так:
myObject = {
Daario: Naharis,
Victarion: Greyjoy,
Quentyn: Ball
}
Сгенерированный результат будет:
Имя: Даарио
Фамилия: Нахарис
Имя: Виктарион
Фамилия: Грейджой
Имя: Квентин
Фамилия: Бал
Ответ 5
Добавление в SimonHawesome отличный ответ. Я сделал краткую версию, которая использует некоторые из новых функций typescript. Я понимаю, что версия SimonHawesome намеренно многословна, чтобы объяснить основные детали. Я также добавил раннюю проверку, чтобы труба работала для значений falsy. Например, если карта null
.
Обратите внимание, что использование преобразования итератора (как это сделано здесь) может быть более эффективным, поскольку нам не нужно выделять память для временного массива (как это сделано в некоторых других ответах).
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] }));
}
}
Ответ 6
Здесь приведена вариация на некоторые из вышеперечисленных ответов, которые поддерживают несколько преобразований (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;
}
}
Использование
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>
Ответ 7
Обновлено: Angular теперь предоставляет канал для прохождения через объект json через значение keyvalue
:
<div *ngFor="let item of myDict | keyvalue">
{{item.key}}:{{item.value}}
</div>
РАБОЧАЯ ДЕМО, и для более подробной информации читайте
Ранее (для более старой версии): до сих пор лучший/самый короткий ответ, который я нашел, - это (без какого-либо фильтра труб или пользовательской функции со стороны компонентов)
Компонентная сторона:
objectKeys = Object.keys;
Сторона шаблона:
<div *ngFor='let key of objectKeys(jsonObj)'>
Key: {{key}}
<div *ngFor='let obj of jsonObj[key]'>
{{ obj.title }}
{{ obj.desc }}
</div>
</div>
РАБОЧАЯ ДЕМО
Ответ 8
У меня была аналогичная проблема, построенная что-то для объектов и 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;
}
}
Ответ 9
Angular 2.x && Angular 4.x не поддерживает это из коробки
Вы можете использовать эти два канала для итерации клавишей или значением.
Ключ:
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)
}
}
Значения:
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])
}
}
Как использовать:
let data = {key1: 'value1', key2: 'value2'}
<div *ngFor="let key of data | keys"></div>
<div *ngFor="let value of data | values"></div>
Ответ 10
Если кому-то интересно, как работать с многомерным объектом, вот решение.
позволяет предположить, что у нас есть следующий объект в службе
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;
}
в компоненте добавить следующую функцию
challenges;
constructor(testService : TestService){
this.challenges = testService.getChallenges();
}
keys() : Array<string> {
return Object.keys(this.challenges);
}
в конце концов, следуйте
<div *ngFor="#key of keys();">
<h4 class="heading">{{challenges[key].title}}</h4>
<p class="description">{{challenges[key].description}}</p>
</div>
Ответ 11
Я рвал на себе волосы, пытаясь разобрать и использовать данные, возвращаемые из запроса JSON/API-интерфейса. Я не уверен точно, где я ошибался, я чувствую, что обводил ответ в течение нескольких дней, преследуя различные коды ошибок, такие как:
"Не удалось найти объект поддержки трубы" iterableDiff ""
"Универсальный массив типов требует один аргумент (ы)"
Ошибки синтаксического анализа JSON, и я уверен, что другие
Я предполагаю, что у меня просто была неправильная комбинация исправлений.
Итак, здесь есть краткое изложение ошибок и вещей, которые нужно искать.
Во-первых, проверьте результат ваших вызовов API, ваши результаты могут быть в форме объекта, массива или массива объектов.
я не буду вдаваться в подробности, достаточно сказать, что оригинальная OP Ошибка не повторяемости, как правило, вызвана тем, что вы пытаетесь выполнить итерацию объекта, а не массива.
Вот некоторые из моих результатов отладки, показывающие переменные как массивов, так и объектов
Поэтому, поскольку мы обычно хотим перебрать наш результат JSON, мы должны убедиться, что он представлен в виде массива. Я пробовал множество примеров, и, возможно, зная, что я знаю сейчас, некоторые из них на самом деле будут работать, но подход, с которым я пошел, действительно заключался в реализации канала, и код, который я использовал, был опубликован 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;
Честно говоря, я думаю, что одной из вещей, которые меня привлекли, было отсутствие обработки ошибок, добавив вызов return undefined, который, я полагаю, теперь мы разрешаем отправлять непредвиденные данные в канал, что, очевидно, происходило в моем случае.,
если вы не хотите иметь дело с аргументом в трубу (и посмотрите, я не думаю, что это необходимо в большинстве случаев), вы можете просто вернуть следующее
if (!obj)
return undefined;
return Object.keys(obj);
Некоторые заметки о создании канала и страницы или компонента, использующего этот канал
я получаю ошибки о том, что name_of_my_pipe не найден
Используйте команду 'ionic generate pipe из CLI, чтобы убедиться, что pipe modules.ts созданы и на них правильно ссылаются. убедитесь, что вы добавили следующее на страницу mypage.module.ts.
import { PipesModule } from ‘…/…/pipes/pipes.module;
(не уверен, что это изменится, если у вас также есть свой собственный custom_module, вам также может понадобиться добавить его в custommodule.module.ts)
если вы использовали команду 'ionic generate page' для создания своей страницы, но решили использовать эту страницу в качестве главной, не забудьте удалить ссылку на страницу из app.module.ts (здесь я опубликовал еще один ответ, касающийся этого https://forum.ionicframework.com/t/solved-pipe-not-found-in-custom-component/95179/13?u=dreaser
В моем поиске ответов есть несколько способов отображения данных в html файле, и я не понимаю достаточно, чтобы объяснить различия. Вы можете найти более подходящим использование одного над другим в определенных сценариях.
<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>
Однако то, что позволило мне отобразить и значение, и ключ, было следующим:
<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>
чтобы сделать вызов API, похоже, вам нужно импортировать HttpModule в app.module.ts
import { HttpModule } from '@angular/http';
.
.
imports: [
BrowserModule,
HttpModule,
и вам нужно Http на странице, с которой вы звоните
import {Http} from '@angular/http';
Делая вызов API, вы, кажется, можете получить доступ к дочерним данным (объектам или массивам в массиве) 2 разными способами, либо, похоже, работает
либо во время разговора
this.http.get('https://SomeWebsiteWithAPI').map(res => res.json().anyChildren.OrSubChildren).subscribe(
myData => {
или когда вы назначаете данные для вашей локальной переменной
posts: Array<String>;
this.posts = myData['anyChildren'];
(не уверен, что эта переменная должна быть строкой массива, но это то, что у меня есть сейчас. Может работать как более общая переменная)
И последнее замечание: не было необходимости использовать встроенную библиотеку JSON, однако вы можете найти эти два вызова удобными для преобразования объекта в строку и наоборот.
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);
Я надеюсь, что эта подборка информации поможет кому-то.
Ответ 12
В JavaScript это переведет объект, который с данными может выглядеть следующим образом:
Интерфейсы в TypeScript представляют собой конструкцию времени dev (чисто для инструментальной обработки... 0). Вы должны написать тот же TypeScript, что и ваш JavaScript.
Ответ 13
Словарь - это объект, а не массив. Я считаю, что ng-repeat требует массив в Angular 2.
Простейшим решением было бы создать канал/фильтр, который преобразует объект в массив "на лету". Тем не менее, вы, вероятно, захотите использовать массив, как говорит @basarat.
Ответ 14
Если у вас есть es6-shim
или ваш tsconfig.json
target es6
, вы можете использовать ES6 Map, чтобы сделать это.
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>
Ответ 15
Определите MapValuesPipe
и реализуйте 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;
}
}
Добавьте свою трубу в свой модуль труб. Это важно, если вам нужно использовать одну и ту же трубу в нескольких компонентах:
@NgModule({
imports: [
CommonModule
],
exports: [
...
MapValuesPipe
],
declarations: [..., MapValuesPipe, ...]
})
export class PipesAggrModule {}
Затем просто используйте канал в html с *ngFor
:
<tr *ngFor="let attribute of mMap | mapValuesPipe">
Помните, что вам нужно будет объявить свой PipesModule в компоненте, где вы хотите использовать канал:
@NgModule({
imports: [
CommonModule,
PipesAggrModule
],
...
}
export class MyModule {}
Ответ 16
//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>
Ответ 17
Поэтому я собирался реализовать свою собственную вспомогательную функцию, objLength (obj), которая возвращает просто Object (obj).keys.length. Но затем, когда я добавлял его в функцию шаблона * ngIf, моя IDE предложила objectKeys(). Я попробовал, и это сработало. Следуя его декларации, он предлагается lib.es5.d.ts, так что поехали!
Вот как я это реализовал (у меня есть пользовательский объект, который использует сгенерированные на стороне сервера ключи в качестве индекса для загруженных файлов):
<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>