遍历Angular中的对象


130

我正在尝试在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中有效)还是在对象上迭代的语法不同?


什么是“词典”?我从未在JavaScript,Angular或TypeScript上下文中看到或听到过该术语。Y

我认为字典是一种地图,在JS上下文中根本不使用该术语,但在Python或Ruby中确实使用了该术语。
塞萨尔·罗德里格斯

2
我认为@bersling答案现在是此问题的正确答案。
Joshua Kissoon

1
请更好地标记正确答案。狂暴是正确的
activedecay

Answers:


86

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

根据MiškoHevery(参考):

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

该计划是要有一个mapToIterable管道

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

1
我在keyas numbervalueas 上尝试相同的对象,string但是角度抛出错误expression has changed after it was checked?为什么会有这样的主意?
Pardeep Jain

是的,这也在我身上发生。同样,如果我也使用@obsur的解决方案。
user2294382 '16

1
请查看bersling的答案,因为在最新的angular 7上有一个简单的解决方案
activedecay

156

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

1
@toioski谢谢,我已经添加了它并更新为for循环的新语法。
bersling

很好的答案,用于ngFor我的字典。我必须做keyValuePair.val [0],尽管我的值以[{}]而不是{}
结尾

1
相对于此,这有优势return Object.keys(dict).map(key => ({key, val: dict[key]}))吗?
贾斯汀·摩根

我什么也没看到,实际上我会用你的方式!
凌晨

72

尝试使用此管道

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>

5
精妙,如果我想保留对键的引用,我将只用键和值映射对象。我希望我可以将几个答案标记为可接受的答案,因为这是我的问题的解决方案,而标记的答案是我的问题的答案。
Rickard Staaf

1
@obscur-如果现在执行上述操作,则会使用angular2.beta.0.0出现错误“检查后表达式已更改”。有什么想法吗?
user2294382 '16

那是因为纯:错误需要更改检测才能注入策略
Judson Terrell

1
为什么将其设置为不纯?
tom10271 '16

这对我来说很好。唯一的事情是我不能在ngFor中使用#。用let代替。
MartinJH

19

除了@obscur的答案外,这里还提供了一个示例,说明如何从@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
}

产生的结果将是:

名:达里奥(Daario)
姓:Naharis

名字:Victarion
姓:葛雷乔伊

名:Quentyn
姓:Ball


2
只需提及一件事,您需要将View更改为<li *ngFor="let u of myObject | keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>。向我+1。
sib10

map函数中的代码可以简化为: return { 'key' : key, 'value' : value[key] };
Makotosan,

17

更新: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>

工作演示


1
let item of myDict | keyvalue这解决了我的问题。
西兰布拉桑RD

13

添加到SimonHawesome的出色答案中。我制作了一个简洁的版本,它利用了一些新的打字稿功能。我意识到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] }));
    }
}

3
喜欢这个主题,在另一个主题之上构建一个评论!看到您的代码时,我将写同样的东西
David

3
该解决方案中唯一的一件事:它应该实施PipeTransform
iRaS

@iRaS好点。我已经更新了答案。我还返回undefined而不是null。
Frederik Aalund

9

这是上述一些答案的一种变体,它支持多种转换(键值,键,值):

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>

1
pure: false对于即时反射非常重要。
的Firat KUCUK

4

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

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


1
这很好用,除了您应该在TypeScript中添加implements PipeTransform类定义
GeorgDangl16年

3

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>

2

如果有人想知道如何使用多维对象,这是解决方案。

假设我们有以下服务对象

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>

2

我一直在努力解析和使用从JSON查询/ api调用返回的数据。我不确定自己出了问题的确切位置,我觉得自己已经连续几天盘旋答案,追逐各种错误代码,例如:

“找不到'iterableDiff'管道支持对象”

“通用TYpe数组需要一个参数”

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”的错误

在CLI中使用“ ionic generate pipe”命令,以确保正确创建和引用了管道模块。确保将以下内容添加到mypage.module.ts页面。

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

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

如果您使用“离子生成页面”命令来创建页面,但决定使用该页面作为主页,请记住从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);

我希望这些信息汇编可以帮助某人。


2
//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>

1

在JavaScript中,这将转换为带有数据的对象,看起来像这样

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


对,那不是真的。类型脚本会强制您编写更简洁的代码。它更容易抽象类。你根本没有。C ++编译为某种asm-asm也没有类甚至类型,但是您编写的c ++则不同于您的asm代码:P
YAMM

1

字典是一个对象,而不是数组。我相信ng-repeat在Angular 2中需要一个数组。

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


1

如果你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>

0

定义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 {}

0

因此,我要实现自己的辅助函数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>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.