Categories: angular, typescript, object

access key and value of object using *ngFor

17 answers

I am a bit confused about how to get the key and value of an object in angular2 while using *ngFor for iterating over the object. I know in angular 1.x there is a syntax like

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

but I don't know how to do the same in angular2. I have tried something similar, without success:

<ul>   <li *ngFor='#key of demo'>{{key}}</li> </ul>  demo = {     'key1': [{'key11':'value11'}, {'key12':'value12'}],     'key2': [{'key21':'value21'}, {'key22':'value22'}],   } 

Here is a plnkr with my attempt: http://plnkr.co/edit/mIj619FncOpfdwrR0KeG?p=preview

How can I get key1 and key2 dynamically using *ngFor? After searching extensively, I found the idea of using pipes but I don't know how to go about it. Is there any inbuilt pipe for doing the same in angular2?

All answers to this question, which has the identifier 35534959

The best answer:

Have Object.keys accessible in the template and use it in *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(){} } 

As in latest release of Angular (v6.1.0) , Angular Team has added new built in pipe for the same named as keyvalue pipe to help you iterate through objects, maps, and arrays, in the common module of angular package. For example -

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

Working Forked Example

check it out here for more useful information -

If you are using Angular v5 or below or you want to achieve using pipe follow this answer

You could create a custom pipe to return the list of key for each element. Something like that:

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

and use it like that:

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

Edit

You could also return an entry containing both key and value:

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

and use it like that:

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

Update

In 6.1.0-beta.1 KeyValuePipe was introduced https://github.com/angular/angular/pull/24319

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

Plunker Example

Previous version

Another approach is to create NgForIn directive that will be used like:

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

Plunker Example

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

From Angular 6.1 you can use the keyvalue pipe:

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

But it has the inconvenient that sorts the resulting list by the key value. If you need something neutral:

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

Don't forget to specify the pure:false pipe attribute. In this case, the pipe is invoked on each change-detection cycle, even if the input reference has not changed (so is the case when you add properties to an object).

Elaboration of @Thierry's answer with example.

There is no inbuilt pipe or method to get key and value from the *ngFor loop. so we have to create custom pipe for the same. as thierry said here is the answer with code.

** The pipe class implements the PipeTransform interface's transform method that takes an input value and an optional array of parameter strings and returns the transformed value.

** The transform method is essential to a pipe. The PipeTransform interface defines that method and guides both tooling and the compiler. It is optional; Angular looks for and executes the transform method regardless. for more info regards pipe refer here

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

and HTML part is:

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

Working Plnkr http://plnkr.co/edit/50LlK0k6OnMnkc2kNHM2?p=preview

update to RC

as suggested by user6123723(thanks) in comment here is update.

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

@Marton had an important objection to the accepted answer on the grounds that the pipe creates a new collection on each change detection. I would instead create an HtmlService which provides a range of utility functions which the view can use as follows:

@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... } 

If you're already using Lodash, you can do this simple approach which includes both key and value:

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

In the typescript file, include:

import * as _ from 'lodash'; 

and in the exported component, include:

_: any = _; 

Thanks for the pipe but i had to make some changes before i could use it in angular 2 RC5. Changed the Pipe import line and also added type of any to the keys array initialization.

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

None of the answers here worked for me out of the box, here is what worked for me:

Create pipes/keys.ts with contents:

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

Add to app.module.ts (Your main module):

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

and then add to your module declarations array something like this:

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

Then in your view template you can use something like this:

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

Here is a good reference I found if you want to read more.

There's a real nice library that does this among other nice pipes. It's called ngx-pipes.

For example, keys pipe returns keys for an object, and values pipe returns values for an object:

keys pipe

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

values pipe

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

No need to create your own custom pipe :)

Use index:

<div *ngFor="let value of Objects; index as key"> 

Usage:

{{key}} -> {{value}} 

Here is the simple solution

You can use typescript iterators for this

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

change demo type to array or iterate over your object and push to another array

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

and from html:

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

I think Object.keys is the best solution to this problem. For anyone that comes across this answer and is trying to find out why Object.keys is giving them ['0', '1'] instead of ['key1', 'key2'], a cautionary tale - beware the difference between "of" and "in":

I was already using Object.keys, something similar to this:

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

However, instead of

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

I had inadvertently wrote

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

which "worked" perfectly fine without any error and returned

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

That cost me about 2 hours googling and cursing..

(slaps forehead)

you can get dynamic object's key with by trying this

myObj['key'] 

You have to do it like this for now, i know not very efficient as you don't want to convert the object you receive from 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;      }); 

Last questions

how do i remove the switch on my home screen?
how to edit the JS date and time to update atuomatically?
How to utilize data stored in a multidimensional array
Powermockito not mocking URL constructor in URI.toURL() method
Android Bluetooth LE Scanner only scans when phone's Location is turned on in some devices
docker wordpress container can't connect to mysql container
How can I declare a number in java that is more than 64-bits? [duplicate]
Optaplanner solutionClass entityCollectionProperty should never return null error when simple JSON object passed to controller
Anylogic, get the time a pedestrain is in a queue
How do I fix this syntax issue with my .flex file?
Optimizing query in PHP
How to find the highest number of a column and print two columns of that row in R?
Ideas on “Error: Type com.google.firebase.iid.zzav is referenced as an interface from com.google.firebase.messaging.zzd”?
JCIFS SmbFile.exists() and SmbFile.isDirectory() return false when it exists and I can listFiles()
PHP total order
Laravel booking system design
neural net - undefined column selected
How to indicate y axis does not start from 0 in ggplot?
Fragments in backStack
Spinner how to change the data