access key and value of object using *ngFor

612,443

Solution 1

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>

To keep original order, use keyvalue:onCompare,
and in component define callback:

// ...
import {KeyValue} from '@angular/common';

@Component(/* ... */)
export class MyComponent {
  private onCompare(_left: KeyValue<any, any>, _right: KeyValue<any, any>): number {
    return -1;
  }
}

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

Solution 2

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(){}
}

Solution 3

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>

Solution 4

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

Solution 5

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

Share:
612,443
Pardeep Jain
Author by

Pardeep Jain

JavaScript developer | Contributor | Writer. | Angular ❤️ | I'm looking for projects to work on. Reach me out for any work here [email protected] Twitter | LinkedIn | YouTube | Facebook | Github | Medium | Quora Angular Community India | Ranked 1 as a contributor in India for Angular

Updated on May 07, 2022

Comments

  • Pardeep Jain
    Pardeep Jain about 2 years

    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?

    • Pankaj Parkar
      Pankaj Parkar about 8 years
      currently there is not support key, value pair kind of syntax in angular2 ngFor, you should look at this answer
    • Pardeep Jain
      Pardeep Jain about 8 years
      @PankajParkar yeah already read this answer. any alternate for now ?
    • Pankaj Parkar
      Pankaj Parkar about 8 years
      @Pradeep I don't think of any other way for this now, you should go for creating own Pipe for this..
    • Pardeep Jain
      Pardeep Jain about 8 years
      hmm but i have no idea how to create pipe for the same.
    • Pankaj Parkar
      Pankaj Parkar about 8 years
      @Pradeep answer which I gave you for reference, has that implementation. they should work..
    • Pardeep Jain
      Pardeep Jain about 8 years
      no i tried the the answer with max upvote but did't run that pipe
    • Pankaj Parkar
      Pankaj Parkar about 8 years
      Cool.. I'll look at plunkr once i have access to machine..
    • David Dahan
      David Dahan over 6 years
      Am I the only one to be surprised there is no easier way to do this in 2017?
    • Egle Kreivyte
      Egle Kreivyte over 6 years
      this.keys = Object.keys(this.demo)
  • Thierry Templier
    Thierry Templier about 8 years
    What do you mean regarding async? Perhaps its use in my answer is ambiguous. The pipe doesn't need it. You can use pipe if the object you want to iterate over is an observable...
  • user6123723
    user6123723 almost 8 years
    This needs to be updated: Here's the warning I get "#" inside of expressions is deprecated. Use "let" instead! (" </li>--> <ul *ngIf="demo"> <li [ERROR ->]*ngFor='#key of demo| keys'> Key: {{key.key}}, value: {{key.value}} </li> "): myComponent@56:6
  • Ankit Maheshwari
    Ankit Maheshwari over 7 years
    Thanks! Also, Good explanation here, check this - How to use Pipes! medium.com/front-end-hacking/…
  • Pardeep Jain
    Pardeep Jain over 7 years
    yeah imports has been changed
  • golyo
    golyo over 7 years
    note the missing closing bracket in keys.push({key: key, value: value[key]);
  • Akzidenzgrotesk
    Akzidenzgrotesk over 7 years
    Not sure if this is new, but to cite from the docs: > We must include our pipe in the declarations array of the AppModule.
  • martin
    martin over 7 years
    I actually discourage anyone from using pipes to create collections inside *ngFor expression. It creates huge performance bottleneck because it needs to generate the collection every time the change detector checks for changes.
  • aruno
    aruno over 7 years
    and how is that better than just Object.keys(...) inside the *ngFor?
  • Stephen Paul
    Stephen Paul over 7 years
    Because it will throw: TypeError: Cannot read property 'keys' of undefined. It doesn't seem to be supported in the template.
  • J. Adam Connor
    J. Adam Connor over 7 years
    This works very well as a solution and avoids the performance issues pointed out above. stackoverflow.com/questions/35534959/…
  • Pardeep Jain
    Pardeep Jain over 7 years
    sorry but no need to use extra library like Lodash for such things. anyways new methods are always welcome :)
  • tomtastico
    tomtastico about 7 years
  • ncohen
    ncohen about 7 years
    Thanks for the solution...the problem is that whenever the object changes, the pipe doesn't update. If I add pure:false to the pipe, it becomes very inefficient. Do you have a solution to update the pipe manually whenever I change the object (remove item)?
  • Pardeep Jain
    Pardeep Jain about 7 years
    may i know what is the difference betwee your answer and other answer's (using pipe only) provided above ? it seems same as above
  • cjohansson
    cjohansson about 7 years
    Sure 1. The examples above uses *ngFor="#entry" instead of *ngFor="let entry of" and my compiler didn't accept the #entry syntax, the reference doesn't use # either. "let entry of (myData | keys)" seems to be a better solution. 2. My compiler didn't validate the example Pipe Class either because it was missing explicit data types so I added that. 3. The examples above doesn't show how to integrate the Pipe into a project which my answer does, you need to import it into the main module.
  • Pardeep Jain
    Pardeep Jain about 7 years
    haha yes offcourese, because when answer was given at that time syntax including # etc. btw your answer is also correct no doubt
  • Pardeep Jain
    Pardeep Jain almost 7 years
    good alternate, but thing is why to use external library for simple peace of code if we can do this using simple piece of code like pipe
  • RichieRock
    RichieRock almost 7 years
    Umm... but it is a pipe? It's just one line in your package.json and another two lines in your module when you import the library. On the other hand, a custom pipe needs a separate file with some 10-20 lines of code and also the import lines in your module. We find using ngx-pipes very easy in our projects. Why should we reinvent the wheel? :)
  • Pardeep Jain
    Pardeep Jain almost 7 years
    yeah no doubt, actually its an opinion based, you can choose either between these two, no one is wrong way.
  • Aakash Thakur
    Aakash Thakur almost 7 years
    Lovely answer Thierry. What changes do I need to make to make it work for an object array like [{"animal":"dog", "country":"france", "car":"nano"}]
  • Thierry Templier
    Thierry Templier almost 7 years
    Thanks! You can add a first ngFor to iterate over the array and put the ngFor with the pipe in it... Hope it answers your question!
  • Experimenter
    Experimenter over 6 years
    The answer is a little bit outdated. The line *ngFor="#entry of content | keys" doesn't work properly and the for ... in loop better to change to "for (const key of Object.keys(value)) "
  • Rach Chen
    Rach Chen over 6 years
    ngFor has been removed. Need to change to ngForOf. <ng-template ngFor [ngForOf]="content" let-c=""> <tr>{{c}}</tr> </ng-template>
  • mwld
    mwld over 6 years
    @RachChen Not in templates: 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
    mwld over 6 years
    @martin Would that actually be such a performance loss when using just Object.keys() to retrieve the keys within the pipe?
  • Aous1000
    Aous1000 over 6 years
    This is a better and more efficient solution
  • danwellman
    danwellman over 6 years
    Don't forget, if you write a custom pipe, you must test that custom pipe as well. So that's 10-20 lines of pipe code, and then probably 20-40 lines of test code to test the pipe.
  • Frank
    Frank about 6 years
    @tomtastico How would you display this for a 3D array? For example {"1": {"1.1": ["1.1.1","1.1.2"]}}. And then nesting 3 ngFor's
  • tomtastico
    tomtastico about 6 years
    @Frank you just said it yourself. Nest the *ngFors. First two using objectKeys, innermost no need (as it's just an array).
  • klonq
    klonq about 6 years
    You should use keys.unshift(...) instead of keys.push(...), this will preserve the order of the keys in the loop.
  • Pardeep Jain
    Pardeep Jain almost 6 years
    This is not a appropriate method, this can be easily done by anyone.
  • ruffin
    ruffin almost 6 years
    Though recall: "Maps have no orders in keys and hence their iteration is unpredictable." You should probably sort the keys in objectKeys to make the sticklers happy. ;^) Pretty sure the new pipe does.
  • Pardeep Jain
    Pardeep Jain almost 6 years
    @Dolan I wish I could accept two answers, But at the time of writting accepted answer is correct one :)
  • JAC
    JAC almost 6 years
    Awesome. Setting objectKeys = Object.keys is simplest method I've seen to be able to check the length of an object from the HTML.
  • danday74
    danday74 almost 6 years
    lol I had to do an ng6 update just to access this pipe - great stuff - thx
  • Pardeep Jain
    Pardeep Jain almost 6 years
    Already shared the same answer above stackoverflow.com/a/51491848/5043867
  • mike-shtil
    mike-shtil over 5 years
    You can keep the original key order using a custom comparator: *ngFor="let item of testObject | keyvalue:keepOriginalOrder" and in your class define: public keepOriginalOrder = (a, b) => a.key
  • Kumaresan Perumal
    Kumaresan Perumal over 5 years
    public keepOriginalOrder = (a, b) => a.key thx a lot for this
  • calios
    calios about 5 years
    this should be the answer - working well on angular 7
  • Pardeep Jain
    Pardeep Jain about 5 years
    That is something new to me, Better If you could add example along with your answer :) Also can you point me to any documentation for the same?
  • Carlos Pinzón
    Carlos Pinzón almost 5 years
    Unbelievable this wasn't out there since the first version
  • Scaramouche
    Scaramouche over 4 years
    hello, can this b used not in the template option, but in the template's actual html code? thanks
  • UI_Dev
    UI_Dev over 4 years
    Great Answer! I was searching for a long time for this
  • Jonathan
    Jonathan over 4 years
    While it works, I get this error in Visual Code: "Member 'objectKeys' is not callable Angular"
  • Os3
    Os3 over 4 years
    keepOriginalOrder the best part
  • Basil
    Basil about 4 years
    What is the type of Objects ? Array or Map? Please make it clear. Thanks in advance
  • Pardeep Jain
    Pardeep Jain almost 4 years
    Would be great if you add working example as well, stackblitz may be
  • Juliyanage Silva
    Juliyanage Silva almost 4 years
    @PardeepJain, I would for far more complex ones.
  • Raphaël Balet
    Raphaël Balet over 3 years
    In this example, the "key" is the index. This have nothing to do with the question and wont work to access the real key
  • Robbie Smith
    Robbie Smith over 3 years
    So the reason I'm here is because Canada decided to give their Provinces with two-letter abbreviations and the written order vs Province abbreviation for Northwest Territories (NT) and Nova Scotia (NS) don't line up. "Nor" comes before "Nov", but NS comes before NT.
  • Admin
    Admin about 3 years
    hi @PardeepJain do you have an answer for this one? stackoverflow.com/questions/66395350/…
  • minigeek
    minigeek about 3 years
    are you serious?
  • minigeek
    minigeek about 3 years
    @PardeepJain please let other people share as well :) ! second part of answer is what I needed
  • minigeek
    minigeek about 3 years
    @mike-shtil that comment was lit! thanks man that is exactly what I needed but worried if it is impacting performance as looping twice
  • Pardeep Jain
    Pardeep Jain about 3 years
    @minigeek different solutions are always welcome mate. But when I have posted the comment this second part you referring was missing and only first part is there which is duplucate of accepted answer. you can check changelog of answer history.
  • minigeek
    minigeek about 3 years
    @PardeepJain. Yes your answer and that guy's comment only helped me fix thing. I understand ur point how plagiarism feels :p
  • 3gwebtrain
    3gwebtrain over 2 years