【问题标题】:Iterate over object in Angular [duplicate]迭代Angular中的对象[重复]
【发布时间】: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 tokenCannot find 'iterableDiff' pipe supporting object 之类的错误

我在这里缺少什么?这不再可能了吗? (第一种语法在 Angular 1.x 中有效)还是迭代对象的语法不同?

【问题讨论】:

  • 什么是“字典”?我从未在 JavaScript、Angular 或 TypeScript 上下文中看到或听到过这个术语。是的
  • 字典是我认为的地图,这个词在 JS 上下文中根本没有使用,但在 Python 或 Ruby 中确实使用了。
  • 我认为@bersling 的答案现在是这个问题的正确答案。
  • 请把正确答案标记好。 bersling 是正确的

标签: angular


【解决方案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 =&gt; ({key, val: dict[key]}))有优势吗?
  • 我没有看到,实际上我会用你的方式!
【解决方案2】:

他们似乎不想支持 ng1 的语法。

根据 Miško Hevery (reference) 的说法:

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

计划是有一个 mapToIterable 管道

&lt;div *ngFor"var item of map | mapToIterable"&gt;

因此,为了迭代您的对象,您需要使用“管道”。 目前还没有实现 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 作为 numbervalue 作为 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 访问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
}

生成的结果是:

名字:达里奥
姓氏:纳哈里斯

名字:维克塔利昂
姓氏:葛雷乔伊

名字:昆廷
姓氏:鲍尔

【讨论】:

  • 只需要提到一件事,您需要更改视图:&lt;li *ngFor="let u of myObject | keyValueFilter"&gt;First Name: {{u.key}} &lt;br&gt; Last Name: {{u.value}}&lt;/li&gt;。来自我的 +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 不支持此功能

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

密钥管道:

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-shimtsconfig.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:

              &lt;tr *ngFor="let attribute of mMap | mapValuesPipe"&gt;

              请记住,您需要在要使用管道的组件中声明您的 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>
                  

                  【讨论】:

                    猜你喜欢
                    • 2017-05-02
                    • 2014-04-16
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-07-23
                    • 1970-01-01
                    相关资源
                    最近更新 更多