首页 文章

在Angular中迭代对象

提问于
浏览
84

我正在尝试在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)或者是迭代对象的语法是不同的?

15 回答

  • 64

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

    根据MiškoHevery(reference):

    Map 在密钥中没有订单,因此迭代是不可预测的 . 这在ng1中得到了支持,但我们认为这是一个错误,在NG2中不会支持该计划是有一个mapToIterable管道<div * ngFor“var item of map | mapToIterable”>

    因此,为了迭代您的对象,您需要使用"pipe" . 目前没有pipe实现这样做 .

    作为一种解决方法,这是一个迭代密钥的小例子:

    Component:

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

    尝试使用这个管道

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

    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 .


    Pre-Angular 6 Answer

    正如其他答案所提到的,'s not supported in ngx, so here'是一个与 key-value-pairs 的解决方法:

    管道:

    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

  • 0

    除了@ 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="#u of myObject | 
    keyValueFilter">First Name: {{u.key}} <br> Last Name: {{u.value}}</li>
    

    所以,如果对象看起来像:

    myObject = {
        Daario: Naharis,
        Victarion: Greyjoy,
        Quentyn: Ball
    }
    

    产生的结果将是:

    名字:Daario
    姓氏:Naharis

    名字:Victarion
    姓氏:Greyjoy

    名字:Quentyn
    姓氏:球

  • 3

    添加到SimonHawesome的excellent answer . 我've made an succinct version which utilizes some of the new typescript features. I realize that SimonHawesome'的版本故意冗长,以解释底层细节 . 我还添加了一个早期检查,以便管道适用于falsy值 . 例如,如果 Map 是 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] }));
        }
    }
    
  • 9

    以下是一些支持多种转换(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>
    
  • 1

    我有类似的问题,为对象和 Map 构建了一些东西 .

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

    Angular 2.x && Angular 4.x do not support this out of the box

    您可以使用这两个管道通过 keyvalue 进行迭代 .

    Keys pipe:

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

    Values pipe:

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

    How to use:

    let data = {key1: 'value1', key2: 'value2'}
    
    <div *ngFor="let key of data | keys"></div>
    <div *ngFor="let value of data | values"></div>
    
  • 3

    Updated : Angular is now providing the pipe for lopping through the json Object via keyvalue :

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

    WORKING DEMO ,以及更多详情 Read


    Previously (For Older Version) : Till now the best / shortest answer I found is ( Without any Pipe Filter or Custom function from Component Side )

    组件方面:

    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

  • 46

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

    假设我们在 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>
    
  • 1

    我试图解析并使用从JSON查询/ api调用返回的数据,一直在撕破我的头发 . 我不确定我到底哪里出错了,我觉得我几天都在盘旋答案,追逐各种错误代码,如:

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

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

    JSON解析错误,并确定其他人

    我假设我只是有错误的修复组合 .

    所以这里有一些关于陷阱和要寻找的事情的总结 .

    首先检查api调用的结果,结果可能是对象,数组或对象数组的形式 .

    我不会进入太多,足以说OP的原始错误是不可迭代通常是由于你试图迭代一个对象而不是一个数组 .

    Heres some of my debugging results showing variables of both arrays and objects

    因此,我们通常想要迭代我们的JSON结果,我们需要确保它是以Array的形式 . 我尝试了很多例子,也许知道我现在知道的一些那些实际上会工作,但我采用的方法确实是实现管道,我使用的代码是由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'命令确保正确创建和引用管道模块.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>
    

    然而,有效的方法让我能够显示 Value 和关键点如下:

    <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'];
    

    (不确定该变量是否需要是一个Array String,但这就是我现在所拥有的 . 它可以作为一个更通用的变量工作)

    最后请注意,没有必要使用内置的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);
    

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

  • 1

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

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

  • 18

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

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

  • 13

    如果您有 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>
    
  • 2

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

    然后只需使用 *ngFor 在html中使用管道:

    <tr *ngFor="let attribute of mMap | mapValuesPipe">

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

    @NgModule({
      imports: [
        CommonModule,
        PipesAggrModule
      ],
    ...
    }
    export class MyModule {}
    

相关问题