在Angular中迭代对象

时间:2015-07-18 11:30:36

标签: angular

我正在尝试在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>

在所有情况下,我都会收到错误,例如&#34;意外的令牌&#34;或&#34;无法找到&#39; iterableDiff&#39;管道支撑物&#34;

我在这里缺少什么?这不可能了吗? (第一种语法适用于Angular 1.x)或者是否在迭代对象时语法不同?

18 个答案:

答案 0 :(得分:88)

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回答

正如其他答案所提到的那样,ngx不支持,所以这里有键值对的解决方法:

管道:

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

答案 1 :(得分:74)

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

根据MiškoHevery(reference):

  

地图在密钥中没有订单,因此迭代是不可预测的。   这在ng1中受到支持,但我们认为这是一个错误,不会   在NG2中得到支持

     

计划是拥有mapToIterable管道

     

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

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

答案 2 :(得分:70)

尝试使用此管道

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>

答案 3 :(得分: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
姓氏:Greyjoy

名字:Quentyn
姓氏:球

答案 4 :(得分:13)

添加到SimonHawesome&#39; s 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] }));
    }
}

答案 5 :(得分:9)

以上是一些支持多种转换(keyval,key,value)的上述答案的变体:

class TableViewController: UITableViewController {

    let wordAtIndexPath = ["one", "two", "three"]
    let cellId = "cellId"
    override func viewDidLoad() {
        super.viewDidLoad()

    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        return wordAtIndexPath.count
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let word = wordAtIndexPath[indexPath.row]
        let cell: UITableViewCell = {
            guard let cell = tableView.dequeueReusableCell(withIdentifier: cellId) else {
                return UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: cellId)
            }    
            return cell
        }()

        cell.textLabel?.text = "My cell number"
        cell.detailTextLabel?.text = word
        return cell
    }

}

用法

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

答案 6 :(得分:6)

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

答案 7 :(得分:4)

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

&#13;
&#13;
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;
  }
}
&#13;
&#13;
&#13;

答案 8 :(得分:3)

Angular 2.x&amp;&amp; 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>

答案 9 :(得分:2)

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

&#34;找不到&#39; iterableDiff&#39;管道支撑物&#34;

&#34; Generic TYpe Array需要一个参数&#34;

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;

老实说,我认为其中一件事就是缺少错误处理,通过添加“返回未定义的”#39;打电话我相信我们现在允许将非预期的数据发送到管道,这显然是在我的情况下发生的。

如果你不想处理管道的争论(看起来我认为在大多数情况下都不需要)你可以返回以下内容

       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)

如果您使用了“离子生成”页面&#39;命令创建你的页面,但决定使用该页面作为你的主页面,记得从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'];

(不确定该变量是否需要是一个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);

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

答案 10 :(得分: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>

答案 11 :(得分:1)

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

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

答案 12 :(得分:1)

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

答案 13 :(得分:1)

  

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

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

答案 14 :(得分: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;
    }
}

在管道模块中添加管道。如果您需要使用same pipe in more than one components

,这一点非常重要
@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 {}

答案 15 :(得分:0)

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

答案 16 :(得分: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>

答案 17 :(得分:0)

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

我更喜欢这种方法,因为它“感觉”最像普通的 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>