迭代Angular中的对象[重复]
Posted
技术标签:
【中文标题】迭代Angular中的对象[重复]【英文标题】:Iterate over object in Angular [duplicate] 【发布时间】:2015-10-08 01:17:24 【问题描述】:我正在尝试在 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>
在所有情况下,我都会收到 Unexpected token
或 Cannot find 'iterableDiff' pipe supporting object
之类的错误
我在这里缺少什么?这不再可能了吗? (第一种语法在 Angular 1.x 中有效)还是迭代对象的语法不同?
【问题讨论】:
什么是“字典”?我从未在 JavaScript、Angular 或 TypeScript 上下文中看到或听到过这个术语。是的 字典是我认为的地图,这个词在 JS 上下文中根本没有使用,但在 Python 或 Ruby 中确实使用了。 我认为@bersling 的答案现在是这个问题的正确答案。 请把正确答案标记好。 bersling 是正确的 【参考方案1】:Angular 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
您不需要在任何模块中注册它,因为 Angular 管道可以在任何模板中开箱即用。
它也适用于Javascript-Maps。
【讨论】:
你应该在类定义中添加implements PipeTransform
(见angular.io/guide/pipes#custom-pipes)
@toioski 谢谢,我已经添加并更新了 for 循环的新语法。
很好的答案,用这个来 ngFor 我的字典。我不得不做 keyValuePair.val[0] 虽然我的价值观最终是 [] 而不是
这比return Object.keys(dict).map(key => (key, val: dict[key]))
有优势吗?
我没有看到,实际上我会用你的方式!【参考方案2】:
他们似乎不想支持 ng1 的语法。
根据 Miško Hevery (reference) 的说法:
地图在键中没有顺序,因此它们的迭代是不可预测的。 这在 ng1 中得到了支持,但我们认为这是一个错误,不会 在 NG2 中得到支持
计划是有一个 mapToIterable 管道
<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
【讨论】:
我正在尝试使用key
作为 number
和 value
作为 string
的对象,但 angular 正在抛出错误 expression has changed after it was checked
?为什么这么想?
是的,这也发生在我身上。如果我也使用@obsur 的解决方案,也是如此。
请看 bersling 的回答,因为在最新的 angular 7 上有一个简单的解决方案【参考方案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>
【讨论】:
太棒了,如果我想保留对键的引用,我只会用键和值映射一个对象。我希望我可以将几个答案标记为已接受的答案,因为这是我的问题的解决方案,而标记的答案是我的问题的答案。 @obscur - 如果我现在执行上述操作,我会使用 angular2.beta.0.0 收到错误“检查后表达式已更改”。有什么想法吗? 那是因为 pure: false 需要注入变更检测策略 为什么要设置为不纯? 这对我来说效果很好。唯一的问题是我不能在 ngFor 中使用 #。用 let 代替。【参考方案4】:更新:Angular 现在提供了通过 keyvalue
遍历 json 对象的管道:
<div *ngFor="let item of myDict | keyvalue">
item.key:item.value
</div>
WORKING DEMO 以及更多详细信息 Read
以前(对于旧版本):到目前为止,我找到的最好/最短的答案是(没有任何管道过滤器或组件端的自定义功能)
组件端:
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>
WORKING DEMO
【讨论】:
let item of myDict | keyvalue
这解决了我的问题。【参考方案5】:
除了@obscur 的回答之外,这里还有一个示例,说明如何从@View 访问key
和value
。
管道:
@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
生成的结果是:
名字:达里奥 姓氏:纳哈里斯
名字:维克塔利昂 姓氏:葛雷乔伊
名字:昆廷 姓氏:鲍尔
【讨论】:
只需要提到一件事,您需要更改视图:<li *ngFor="let u of myObject | keyValueFilter">First Name: u.key <br> Last Name: u.value</li>
。来自我的 +1。
地图函数中的代码可以简化为:return 'key' : key, 'value' : value[key] ;
【参考方案6】:
添加到 SimonHawesome 的 excellent answer。我制作了一个简洁的版本,它利用了一些新的打字稿功能。我意识到 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] ));
【讨论】:
喜欢这个帖子,一个评论建立在另一个之上!当我看到你的代码时,我正要写同样的东西 这个解决方案中唯一的事情:它应该实现PipeTransform
@iRaS 好点。我已经更新了我的答案。我也返回 undefined 而不是 null。【参考方案7】:
以下是上述一些支持多种转换(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>
【讨论】:
pure: false
对于即时反射非常重要。【参考方案8】:
我遇到了类似的问题,为对象和地图构建了一些东西。
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;
【讨论】:
这很好用,除了在 TypeScript 中你应该将implements PipeTransform
添加到类定义中【参考方案9】:
Angular 2.x && Angular 4.x 不支持此功能
您可以使用这两个管道通过 key 或 value 进行迭代。
密钥管道:
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】://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>
【讨论】:
【参考方案11】:如果有人想知道如何处理多维对象,这里是解决方案。
假设我们在 service
中有以下对象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>
【讨论】:
【参考方案12】:我一直在努力解析和使用从 JSON 查询/api 调用返回的数据。我不确定我到底哪里出错了,我觉得我已经盘旋了好几天的答案,追逐各种错误代码,例如:
“找不到'iterableDiff'管道支持对象”
“通用类型数组需要一个参数”
JSON 解析错误,我确定其他人
我假设我只是有错误的修复组合。
所以这里有一些陷阱和要寻找的东西的摘要。
首先检查你的api调用的结果,你的结果可能是对象、数组或对象数组的形式。
我不会过多介绍它,我只想说 OP 的原始错误不可迭代通常是由于您尝试迭代对象而不是数组引起的。
Heres some of my debugging results showing variables of both arrays and objects
因此,由于我们通常希望遍历 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;
老实说,我认为让我失望的一件事是缺乏错误处理,通过添加“返回未定义”调用,我相信我们现在允许将非预期数据发送到管道,这显然正在发生就我而言。
如果您不想处理管道的参数(并且看起来我认为在大多数情况下没有必要),您可以返回以下内容
if (!obj)
return undefined;
return Object.keys(obj);
关于创建管道和使用该管道的页面或组件的一些注意事项
我是否收到关于“name_of_my_pipe”未找到的错误
使用 CLI 中的“ionic generate 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 库 但是,您可能会发现这 2 个调用很方便将对象转换为字符串,反之亦然
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);
我希望这个信息汇编对某人有所帮助。
【讨论】:
【参考方案13】:字典是一个对象,而不是一个数组。我相信 ng-repeat 在 Angular 2 中需要一个数组。
最简单的解决方案是创建一个管道/过滤器,将对象动态转换为数组。也就是说,您可能想像@basarat 所说的那样使用数组。
【讨论】:
【参考方案14】:如果你有es6-shim
或tsconfig.json
目标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】:在 JavaScript 中,这将转换为带有数据的对象,可能看起来像这样
TypeScript 中的接口是一种开发时构造(纯粹用于工具... 0 运行时影响)。您应该编写与 JavaScript 相同的 TypeScript。
【讨论】:
这不是真的,抱歉。 type script 强制你编写更简洁的代码。抽象类要容易得多。你根本没有。 C++ 编译为一些 asm - asm 也没有类甚至类型,但是你编写不同的 c++ 然后你的 asm 代码:P【参考方案16】:定义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;
在您的管道模块中添加您的管道。如果您需要使用same pipe in more than one components,这一点很重要:
@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
【讨论】:
【参考方案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>
【讨论】:
【参考方案18】:还有另一种循环对象的方法,使用结构指令:
我更喜欢这种方法,因为它“感觉”最像普通的 ngFor 循环。 :-)
(例如,在这种情况下,我添加了 Angular 的上下文变量 let i = index | even | odd | first | last | count),这些变量可以在我的循环中访问)。
@Directive(
selector: '[ngForObj]'
)
export class NgForObjDirective implements OnChanges
@Input() ngForObjOf: [key: string]: any ;
constructor(private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef)
ngOnChanges(changes: SimpleChanges): void
if (changes.ngForObjOf && changes.ngForObjOf.currentValue)
// remove all views
this.viewContainerRef.clear();
// create a new view for each property
const propertyNames = Object.keys(changes.ngForObjOf.currentValue);
const count = propertyNames.length;
propertyNames.forEach((key: string, index: number) =>
const even = ((index % 2) === 0);
const odd = !even;
const first = (index === 0);
const last = index === (count - 1);
this.viewContainerRef.createEmbeddedView(this.templateRef,
$implicit: changes.ngForObjOf.currentValue[key],
index,
even,
odd,
count,
first,
last
);
);
在您的模板中使用:
<ng-container *ngForObj="let item of myObject; let i = index"> ... </ng-container>
如果你想循环使用一个整数值,你可以使用这个指令:
@Directive(
selector: '[ngForInt]'
)
export class NgForToDirective implements OnChanges
@Input() ngForIntTo: number;
constructor(private templateRef: TemplateRef<any>, private viewContainerRef: ViewContainerRef)
ngOnChanges(changes: SimpleChanges): void
if (changes.ngForIntTo && changes.ngForIntTo.currentValue)
// remove all views
this.viewContainerRef.clear();
let currentValue = parseInt(changes.ngForIntTo.currentValue);
for (let index = 0; index < currentValue; index++)
this.viewContainerRef.createEmbeddedView(this.templateRef,
$implicit: index,
index
);
在您的模板中使用(例如:从 0 到 14 的循环(= 15 次迭代):
<ng-container *ngForInt="let x to 15"> ... </ng-container>
【讨论】:
以上是关于迭代Angular中的对象[重复]的主要内容,如果未能解决你的问题,请参考以下文章