使用* ngFor访问对象的键和值


425

对于在迭代对象时如何在angular2中获取keyvalue的对象,我有些困惑*ngFor。我知道在angular 1.x中有一种语法

ng-repeat="(key, value) in demo"

但是我不知道如何在angular2中做同样的事情。我尝试过类似的尝试,但没有成功:

<ul>
  <li *ngFor='#key of demo'>{{key}}</li>
</ul>

demo = {
    'key1': [{'key11':'value11'}, {'key12':'value12'}],
    'key2': [{'key21':'value21'}, {'key22':'value22'}],
  }

这是我尝试的plnkr:http ://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

如何获得key1key2动态使用*ngFor?经过广泛搜索之后,我发现了使用管道的想法,但是我不知道该怎么做。在angular2中是否有用于执行相同操作的内置管道?


2
目前key, value在angular2中不支持配对语法ngFor,您应该看一下这个答案
Pankaj Parkar

@PankajParkar是的,已经阅读了此答案。现在还有其他选择吗?
Pardeep Jain

@Pradeep我不认为现在的这个任何其他方式,你应该去创造自己Pipe的这个..
潘卡Parkar

嗯,但我不知道如何创建相同的管道。
Pardeep Jain

我给您提供参考的@Pradeep答案已实现。他们应该工作..
Pankaj Parkar '16

Answers:


398

Object.keys在模板中访问和使用它*ngFor

@Component({
  selector: 'app-myview',
  template: `<div *ngFor="let key of objectKeys(items)">{{key + ' : ' + items[key]}}</div>`
})

export class MyComponent {
  objectKeys = Object.keys;
  items = { keyOne: 'value 1', keyTwo: 'value 2', keyThree: 'value 3' };
  constructor(){}
}


25
这是一个更好,更有效的解决方案
Aous1000

1
@tomtastico如何显示3D阵列?例如{“ 1”:{“ 1.1”:[“ 1.1.1”,“ 1.1.2”]}}。然后嵌套3个ngFor
Frank

@弗兰克,你只是自己说的。嵌套*ngFors。前两个使用objectKeys,最内层不需要(因为它只是一个数组)。
tomtastico

1
太棒了 我已经看到设置objectKeys = Object.keys是最简单的方法,它能够从HTML检查对象的长度。
JAC

391

最新版本的Angular(v6.1.0)一样,Angular Team添加了新的内置管道,其名称与keyvaluepipe 相同,可帮助您在commonangular软件包的模块中迭代对象,地图和数组。例如 -

<div *ngFor="let item of testObject | keyvalue">
    Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>

工作分叉的例子

在这里查看更多有用的信息-

如果您使用的是Angular v5或更低版本,或者您想使用管道来实现,请遵循以下答案


1
大声笑我不得不做一个ng6更新只是为了访问此管道-很棒的东西
-thx

36
您可以使用自定义比较器来保持原始键顺序: *ngFor="let item of testObject | keyvalue:keepOriginalOrder" 并且在您的类中定义: public keepOriginalOrder = (a, b) => a.key
mike-shtil

2
public keepOriginalOrder =(a,b)=> a.key非常
有用

1
这应该是答案-在角度7上工作良好
calios

1
令人难以置信的是,自从第一个版本以来就没有出现过
卡洛斯·平佐(CarlosPinzón)

227

您可以创建一个自定义管道以返回每个元素的键列表。像这样:

import { PipeTransform, Pipe } from '@angular/core';

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push(key);
    }
    return keys;
  }
}

并像这样使用它:

<tr *ngFor="let c of content">           
  <td *ngFor="let key of c | keys">{{key}}: {{c[key]}}</td>
</tr>

编辑

您还可以返回包含键和值的条目:

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push({key: key, value: value[key]});
    }
    return keys;
  }
}

并像这样使用它:

<span *ngFor="let entry of content | keys">           
  Key: {{entry.key}}, value: {{entry.value}}
</span>

1
请注意keys.push({key: key, value: value[key]);
Marton Pallagi'9

49
实际上,我不鼓励任何人使用管道在*ngFor表达式内部创建集合。它创建了巨大的性能瓶颈,因为每次更改检测器检查更改时都需要生成集合。
马丁

3
感谢您的解决方案...问题是,只要对象更改,管道就不会更新。如果将其添加pure:false到管道中,它将变得非常低效。您有解决方案,只要我更改对象(删除项)就可以手动更新管道?
ncohen

4
答案有点过时了。* ngFor =“#entry of content | keys”行无法正常工作,for ... in循环最好更改为“ for(Object.keys(value))的const key”
实验者

2
@RachChen不在模板中:common: NgFor has been removed as it was deprecated since v4. Use NgForOf instead. This does not impact the use of*ngFor in your templates.jaxenter.com/road-to-angular-5-133253.html
mwld

49

更新资料

6.1.0-beta.1 中引入了KeyValuePipe https://github.com/angular/angular/pull/24319

<div *ngFor="let item of {'b': 1, 'a': 1} | keyvalue">
  {{ item.key }} - {{ item.value }}
</div>

柱塞示例

先前版本

另一种方法是创建NgForIn将使用的指令,例如:

<div *ngFor="let key in obj">
   <b>{{ key }}</b>: {{ obj[key] }}
</div>

柱塞示例

ngforin.directive.ts

@Directive({
  selector: '[ngFor][ngForIn]'
})
export class NgForIn<T> extends NgForOf<T> implements OnChanges {

  @Input() ngForIn: any;

  ngOnChanges(changes: NgForInChanges): void {
    if (changes.ngForIn) {
      this.ngForOf = Object.keys(this.ngForIn) as Array<any>;

      const change = changes.ngForIn;
      const currentValue = Object.keys(change.currentValue);
      const previousValue = change.previousValue ? Object.keys(change.previousValue) : undefined;
      changes.ngForOf =  new SimpleChange(previousValue, currentValue, change.firstChange);

      super.ngOnChanges(changes);
    }
  }
}

43

在Angular 6.1中,您可以使用键值管道:

<div *ngFor="let item of testObject | keyvalue">
    Key: <b>{{item.key}}</b> and Value: <b>{{item.value}}</b>
</div>

但是它不方便按键值对结果列表进行排序。如果您需要中立的东西:

@Pipe({ name: 'keyValueUnsorted', pure: false  })
export class KeyValuePipe implements PipeTransform {
  transform(input: any): any {
    let keys = [];
    for (let key in input) {
      if (input.hasOwnProperty(key)) {
        keys.push({ key: key, value: input[key]});
      }
    }
    return keys;
  }
}

不要忘记指定pure:false管道属性。在这种情况下,即使输入引用未更改(在向对象添加属性时也是如此),管道在每个更改检测周期被调用。



26

用示例详细说明@Thierry的答案。

没有key and value从* ngFor循环获取的内置管道或方法。所以我们必须为它创建自定义管道。正如thierry所说的,这就是代码的答案。

**管道类实现了PipeTransform接口的transform方法,该方法接受一个输入值和一个可选的参数字符串数组,并返回转换后的值。

**变换方法对于管道至关重要。PipeTransform接口定义该方法,并指导工具和编译器。它是可选的;Angular无论如何都会寻找并执行transform方法。有关管道的更多信息,请参阅此处

import {Component, Pipe, PipeTransform} from 'angular2/core';
import {CORE_DIRECTIVES, NgClass, FORM_DIRECTIVES, Control, ControlGroup, FormBuilder, Validators} from 'angular2/common';

@Component({
    selector: 'my-app',
    templateUrl: 'mytemplate.html',
    directives: [CORE_DIRECTIVES, FORM_DIRECTIVES],
    pipes: [KeysPipe]
})
export class AppComponent { 

  demo = {
    'key1': 'ANGULAR 2',
    'key2': 'Pardeep',
    'key3': 'Jain',
  }
}


@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
  transform(value, args:string[]) : any {
    let keys = [];
    for (let key in value) {
      keys.push({key: key, value: value[key]});
    }
    return keys;
  }
}

HTML部分是:

<ul>
  <li *ngFor='#key of demo | keys'>
   Key: {{key.key}}, value: {{key.value}}
  </li>
</ul>

工作中的Plnkr http://plnkr.co/edit/50LlK0k6OnMnkc2kNHM2?p=preview

更新到RC

如user6123723所建议(感谢)在此处评论中为更新。

<ul>
  <li *ngFor='let key of demo | keys'>
   Key: {{key.key}}, value: {{key.value}}
  </li>
</ul>

这需要更新:这是我不赞成在表达式内部获取“#”的警告。用“ let”代替!(“ </ li>-> <ul * ngIf =” demo“> <li [ERROR->] * ngFor ='#key of demo | keys'>密钥:{{key.key}},值:{ {key.value}} </ li>“):myComponent @ 56:6
user6123723 '16

不知道这是否是新的,但可以从docs中引用:>我们必须将管道包含在AppModule的声明数组中。
Akzidenzgrotesk '16

18

@Marton 对接受的答案重要的反对意见,理由是管道在每次更改检测时都会创建一个新的集合。相反,我将创建一个HtmlService,它提供一系列实用程序功能,该视图可以按如下方式使用:

@Component({
  selector: 'app-myview',
  template: `<div *ngFor="let i of html.keys(items)">{{i + ' : ' + items[i]}}</div>`
})
export class MyComponent {
  items = {keyOne: 'value 1', keyTwo: 'value 2', keyThree: 'value 3'};
  constructor(private html: HtmlService){}
}

@Injectable()
export class HtmlService {
  keys(object: {}) {
    return Object.keys(object);
  }
  // ... other useful methods not available inside html, like isObject(), isArray(), findInArray(), and others...
}

2
这比仅Object.keys(...)在* ngFor内部更好?
Simon_Weaver

8
因为它会抛出:TypeError: Cannot read property 'keys' of undefined。模板似乎不支持它。
斯蒂芬·保罗

1
这可以很好地作为解决方案,并且可以避免上述性能问题。stackoverflow.com/questions/35534959/...
J.亚当·康纳

你好,这个b不能在template选项中使用,而是在模板的实际html代码中使用吗?感谢
Scaramouche 19'Aug

16

如果您已经在使用Lodash,则可以执行以下简单方法,其中包括键和值:

<ul>
  <li *ngFor='let key of _.keys(demo)'>{{key}}: {{demo[key]}}</li>
</ul>

在打字稿文件中,包括:

import * as _ from 'lodash';

在导出的组件中,包括:

_: any = _;

抱歉,但无需使用类似Lodash的额外库来进行此类操作。无论如何,总是欢迎使用新方法:)
Pardeep Jain

8

感谢管道,但我必须进行一些更改才能在angular 2 RC5中使用它。更改了Pipe导入行,并在key数组初始化中添加了any的类型。

 import {Pipe, PipeTransform} from '@angular/core';

 @Pipe({name: 'keys'})
 export class KeysPipe implements PipeTransform {
 transform(value) {
   let keys:any = [];
   for (let key in value) {
      keys.push( {key: key, value: value[key]} );
    }
     return keys;
  }
}

是的进口已经改变
Pardeep Jain

7

开箱即用的答案对我都不起作用,以下是对我有用的答案:

pipes/keys.ts使用内容创建:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform
{
    transform(value:any, args:string[]): any {
        let keys:any[] = [];
        for (let key in value) {
            keys.push({key: key, value: value[key]});
        }
        return keys;
    }
}

添加到app.module.ts(您的主模块):

import { KeysPipe } from './pipes/keys';

然后将如下所示的内容添加到模块声明数组中:

@NgModule({
    declarations: [
        KeysPipe
    ]
})
export class AppModule {}

然后,在您的视图模板中,您可以使用如下代码:

<option *ngFor="let entry of (myData | keys)" value="{{ entry.key }}">{{ entry.value }}</option>

如果您想了解更多信息,是一个很好的参考。


我可以知道您提供的答案和上面提供的其他答案(仅使用管道)有何区别?似乎与上面相同
Pardeep Jain

1
当然1.上面的示例使用* ngFor =“#entry”代替* ngFor =“ let entry of”,并且我的编译器不接受#entry语法,引用也不使用#。“让(myData |键)进入”似乎是一个更好的解决方案。2.我的编译器也没有验证示例管道类,因为它缺少显式数据类型,因此我添加了它。3.上面的示例没有显示如何将Pipe集成到我的答案中所做的项目中,您需要将其导入主模块中。
cjohansson

哈哈,不错,因为当时答案是包括在内的语法#。顺便说一句,您的答案无疑也是正确的
Pardeep Jain

6

有一个真正的漂亮库在其他漂亮的管道中执行此操作。这就是ngx-pipes

例如,键管道返回对象的键,而值管道返回对象的值:

钥匙管

<div *ngFor="let key of {foo: 1, bar: 2} | keys">{{key}}</div> 
<!-- Output: 'foo' and 'bar -->

价值观管道

<div *ngFor="let value of {foo: 1, bar: 2} | values">{{value}}</div>
<!-- Output: 1 and 2 -->

无需创建自己的自定义管道:)


2
一个很好的选择,但是如果我们可以使用像管道这样的简单代码来做到这一点,那就是为什么要使用外部库来实现简单的代码和平
Pardeep Jain

2
嗯...不过是烟斗吗?导入库时,这只是package.json中的一行,而模块中则为另外两行。另一方面,自定义管道需要一个单独的文件,其中包含大约10-20行代码以及模块中的导入行。我们发现在我们的项目中使用ngx-pipes非常容易。我们为什么要重新发明轮子?:)
RichieRock's

是的,毫无疑问,实际上它是一种观点,您可以在这两者之间选择,没有一个是错误的方法。
帕迪普·in那

2
不要忘了,如果你写一个自定义的管道,你必须测试的定制管。因此,这是10-20行管道代码,然后大概是20-40行测试代码来测试管道。
danwellman


3

这是简单的解决方案

您可以为此使用打字稿迭代器

import {Component} from 'angular2/core';
declare var Symbol;
@Component({
    selector: 'my-app',
    template:`<div>
    <h4>Iterating an Object using Typescript Symbol</h4><br>
Object is : <p>{{obj | json}}</p>
</div>
============================<br>
Iterated object params are:
<div *ngFor="#o of obj">
{{o}}
</div>

`
})
export class AppComponent {
  public obj: any = {
    "type1": ["A1", "A2", "A3","A4"],
    "type2": ["B1"],
    "type3": ["C1"],
    "type4": ["D1","D2"]
  };

  constructor() {
    this.obj[Symbol.iterator] =  () => {
          let i =0;

          return {
            next: () => {
              i++;
              return {
                  done: i > 4?true:false,
                  value: this.obj['type'+i]
              }
            }
          }
    };
  }
}

http://plnkr.co/edit/GpmX8g?p=info


3

将演示类型更改为数组或遍历对象并推送到另一个数组

public details =[];   
Object.keys(demo).forEach(key => {
      this.details.push({"key":key,"value":demo[key]);
    });

和从HTML:

<div *ngFor="obj of details">
  <p>{{obj.key}}</p>
  <p>{{obj.value}}</p>
  <p></p>
</div>

这不是适当的方法,任何人都可以轻松完成。
Pardeep Jain

1

我认为Object.keys是解决此问题的最佳方法。对于遇到此问题并试图找出Object.keys为什么要给它们['0','1']而不是['key1','key2']的人,请注意一个故事-注意“的“和”中:

我已经在使用Object.keys,类似于以下内容:

interface demo {
    key: string;
    value: string;
}

createDemo(mydemo: any): Array<demo> {
    const tempdemo: Array<demo> = [];

    // Caution: use "of" and not "in"
    for (const key of Object.keys(mydemo)) {
        tempdemo.push(
            { key: key, value: mydemo[key]}
        );
    }

    return tempdemo;
}

但是,代替

for (const key OF Object.keys(mydemo)) {

我无意间写了

for (const key IN Object.keys(mydemo)) {

哪个“工作”完美无误,然后返回

[{key: '0', value: undefined}, {key: '1', value: undefined}]

这花了我大约2个小时的时间进行谷歌搜索和诅咒。

(拍打额头)


1

您可以通过尝试获得动态对象的密钥

myObj['key']

0

您现在必须这样做,我知道效率不是很高,因为您不想转换从firebase收到的对象。

    this.af.database.list('/data/' + this.base64Email).subscribe(years => {
        years.forEach(year => {

            var localYears = [];

            Object.keys(year).forEach(month => {
                localYears.push(year[month])
            });

            year.months = localYears;

        })

        this.years = years;

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