以角度迭代物体

我正在尝试做一些事情在角度2阿尔法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 tokenCannot find 'iterableDiff' pipe supporting object这样的错误

我错过了什么?这不可能了吗?(第一种语法是在 Angular 1.x 中工作的)还是在对象上迭代的语法不同?

274304 次浏览

在 JavaScript 中,这将转换为带有数据的对象如下所示

TypeScript 中的接口是一个开发时间构造(纯粹为了工具... 0运行时影响)。您应该编写与 JavaScript 相同的 TypeScript。

字典是一个对象,而不是一个数组。我相信 n- 重复需要一个角度2的数组。

最简单的解决方案是创建一个管道/过滤器,将对象动态转换为数组。也就是说,您可能希望像@basarat 所说的那样使用数组。

看起来他们不想支持 ng1的语法。

根据 Mi ko Hvery (参考文献)的说法:

映射在键中没有顺序,因此它们的迭代是不可预测的。 这是在 ng1中支持的,但我们认为这是一个错误,将不会 NG2支持

我们的计划是有一个地图管道

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

因此,为了在对象上进行迭代,您需要使用一个“管道”。 目前还没有实现这个功能的 烟斗

作为一种解决方案,下面是一个迭代键的小示例:

组成部分:

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
}

试着用这根管子

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>

角度6.1.0 + 答案

像这样使用内置的 keyvalue:

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

斯塔克布利茨: https://stackblitz.com/edit/angular-iterate-key-value

您不需要在任何模块中注册这个,因为在任何模板中都可以使用角管。

它也适用于 Javascript-地图

除了这个问题的答案外,这里还有一个例子,说明如何从@View 访问 keyvalue

烟斗:

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

产生的结果将是:

名叫达里奥
姓: Naharis

名字: 维多利亚
姓: 葛雷乔伊

名字是 Quentyn
姓: 球

我也遇到过类似的问题,为对象和地图建立了一些东西。

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

添加到 SimonHAwesome 的 答得好。我做了一个简洁的版本,它利用了一些新的打印特性。我意识到西蒙哈威尔的版本是故意冗长,以解释潜在的细节。我还添加了一个提前退出检查,以便管道能够为 假的值工作。例如,如果地图是 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] }));
}
}

如果你有 es6-shim或者你的 tsconfig.json目标 es6,你可以使用 ES6地图来制作它。

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>

如果有人想知道如何处理多维对象,这里有一个解决方案。

让我们假设在 服务中有以下对象

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>

下面是上面一些支持多重转换(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>

角度2.x & & 角度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>

我一直在努力解析和使用从 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;

老实说,我认为其中一件让我感到困扰的事情是缺乏错误处理,通过添加“返回未定义”调用,我相信我们现在允许非预期的数据被发送到管道,这显然发生在我的情况下。

如果你不想处理管道的参数(我认为在大多数情况下没有这个必要) ,你可以返回以下内容

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

关于创建管道和使用该管道的页面或组件的一些注释

是我收到了关于“ name _ of _ my _ tube”没有被找到的错误

使用来自 CLI 的“ ionicgeneratetube”命令来确保管道模块的创建和引用正确。确保将以下内容添加到 mypage.module.ts 页面。

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

(如果您还有自己的 custom_ module,则不确定是否会更改,您可能还需要将其添加到 custommodule.module.ts)

如果你使用“ ionic Generator 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 调用时,您似乎能够以两种不同的方式获取子数据(数组中的对象或数组) ,这两种方式似乎都可以工作

不管是打电话的时候

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

我希望这些信息能帮到某些人。

更新: Angular 现在通过 keyvalue为 json 对象提供了切换管道:

<div *ngFor="let item of myDict | keyvalue">
\{\{item.key}}:\{\{item.value}}
</div>

工作演示 ,更多细节 < strong > 阅读


以前(旧版本) : 到目前为止,我找到的最好/最短的答案是(没有任何管道过滤器或自定义功能从组件端)

组成部分:

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>

正在演示

定义 MapValuesPipe并实施 管道变换:

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">

请记住,您需要在您想要使用管道的组件中声明您的管道模块:

@NgModule({
imports: [
CommonModule,
PipesAggrModule
],
...
}
export class MyModule {}
//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>

所以我要实现我自己的 helper 函数 objecLlength (obj) ,它只返回 Object (obj)。钥匙,长度。但是,当我将它添加到模板 * 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>

还有另一种方法来循环对象,使用 结构指令结构指令:

我更喜欢这种方法,因为它“感觉”最像普通的 ngFor 循环. : -)

(在这个例子中,我添加了 Angular 的上下文变量 让 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>