迭代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 tokenCannot 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 =&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>

【讨论】:

以上是关于迭代Angular中的对象[重复]的主要内容,如果未能解决你的问题,请参考以下文章

遍历Angular 8中的对象键[重复]

使用Angular语法删除数组中的对象[重复]

Json对象迭代Angular 2

Angular 2将JSON对象解析为角度类[重复]

在Angular 2中合并对象[重复]

迭代对象并将其显示在表格 Angular 6 中