How can I use ngFor to iterate over Typescript Enum as an array of strings

AngularAngular2 Template

Angular Problem Overview


I'm using Angular2 and TypeScript and I have an enum:

export enum Role {
    ServiceAdmin, CompanyAdmin, Foreman, AgentForeman, 
    CrewMember, AgentCrewMember, Customer
}

I want to use *ngFor to iterate over the enum. What is the best way to do this? Must I create a Pipe? Or is there a simpler way?

Angular Solutions


Solution 1 - Angular

You can just use the "keyvalue" pipe introduced in Angular 6.1.

<p *ngFor="let enum of TestEnum | keyvalue">
  {{ enum.key }} - {{ enum.value}}
</p>

See here for a full example -> https://stackblitz.com/edit/angular-gujg2e

Solution 2 - Angular

An enum is just an object.

Your enum is written something like this in JavaScript:

{
    0: "ServiceAdmin", 
    1: "CompanyAdmin", 
    2: "Foreman", 
    3: "AgentForeman", 
    4: "CrewMember", 
    5: "AgentCrewMember", 
    6: "Customer", 
    ServiceAdmin: 0, 
    CompanyAdmin: 1, 
    Foreman: 2, 
    AgentForeman: 3, 
    CrewMember: 4,
    AgentCrewMember: 5,
    Customer: 6
}

So you can iterate it this way (plnkr):

@Component({
    ...
    template: `
    <div *ngFor="let item of keys()">
      {{ item }}
    </div>  
  `
})
export class YourComponent {
    role = Role;
    keys() : Array<string> {
        var keys = Object.keys(this.role);
        return keys.slice(keys.length / 2);
    }
}

Or would be better to create custom pipe:

@Pipe({
  name: 'enumToArray'
})
export class EnumToArrayPipe implements PipeTransform {
  transform(data: Object) {
    const keys = Object.keys(data);
    return keys.slice(keys.length / 2);
  }
}

Example

Update

Typescript 2.4 allows enum members to contain string initializers like:

enum Colors {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE",
}

in this case you can just return Object.keys(data); from pipe.

Solution 3 - Angular

The scope of the template is the component instance. If you want to access something outside this scope you need to make it available from withing your component instance:

This also works if the enum keys do not start with 0

@Pipe({name: 'enumToArray'})
export class EnumToArrayPipe implements PipeTransform {
  transform(value) : Object {
    return Object.keys(value).filter(e => !isNaN(+e)).map(o => { return {index: +o, name: value[o]}});
  }
}

@Component({
  ...
  imports: [EnumsToArrayPipe],
  template: `<div *ngFor="let item of roles | enumToArray">{{item.index}}: {{item.name}}</div>`
})
class MyComponent {
  roles = Role;
}

See also https://stackoverflow.com/a/35750252/217408

Solution 4 - Angular

I needed to do the same thing and maybe this is what you wanted.
More DRY and it can be used with module too.

export enum Role {
    ServiceAdmin, CompanyAdmin, Foreman, AgentForeman, 
    CrewMember, AgentCrewMember, Customer
}
    
export namespace Role {

  export function keys(): Array<string>{
    var keys = Object.keys(Role);
    return keys.slice(keys.length / 2, keys.length-1);
  }
}

the object output before the slice

{
    "1",
    "2",
    "3",
    "4",
    "5",
    "6",
    "7",
    "ServiceAdmin",
    "CompanyAdmin",
    "Foreman",
    "AgentForeman",
    "CrewMember",
    "AgentCrewMember",
    "Customer",
    "keys"
}

typescript merges the two declarations hence the keys.lenght-1

and the ngFor:

<div *ngFor="let role of Roles.keys()">{{ role }}</div>

more info:
Typescript's Declaration merging

based on:
https://stackoverflow.com/questions/28150739/typescript-add-functions-to-an-enum https://basarat.gitbooks.io/typescript/content/docs/enums.html (at the end of the enums chapter.)

Solution 5 - Angular

After further research and review of the other answers I now can formulate an answer to my question. I think its not possible to just use *ngFor to iterate over an enum without some code support in the component. The code support can consist of constructor code that turns the Enum into some sort of array or we can create a custom pipe that does something similar.

Solution 6 - Angular

export enum Priority {
  LL = 1,   // VERY LOW
  L = 2,    // LOW
  N = 3,    // NORMAL
  U = 4,    // HIGH
  UU = 5    // VERY HIGH
}

Your angular component.ts :

import { Priority } from './../shared/core/config/datas.config';

@Component({
  selector: 'app-yourcomponent',
  template: `
    <ng-container *ngFor="let p of getPriority">
       <div> {{p.key}} / {{p.value}} </div>
    </ng-container> 
  `
})

export class YourComponent {
  getPriority = this.getENUM(Priority);

  getENUM(ENUM:any): string[] {
    let myEnum = [];
    let objectEnum = Object.keys(ENUM);
    const values = objectEnum.slice( 0 , objectEnum.length / 2 );
    const keys = objectEnum.slice( objectEnum.length / 2 );

    for (let i = 0 ; i < objectEnum.length/2 ; i++ ) {
      myEnum.push( { key: keys[i], value: values[i] } ); 
    }
    return myEnum;
  }
}

Solution 7 - Angular

using pipe:

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

@Pipe({
  name: 'enum'
})
export class EnumSelectPipe implements PipeTransform {
  transform(value: any): [number, string][] {
    return Object.keys(value).filter(t => isNaN(+t)).map(t => [value[t], t]);
  }
}

and in the template:

<mat-select formControlName="type" placeholder="Package Type">
  <mat-option *ngFor="let pType of PackageTypes | enum" [value]="pType[0]">{{ pType[1] | title}}</mat-option>
</mat-select>

Solution 8 - Angular

I have the enum:

export enum FileCategory {
  passport = 'Multipass',
  agreement = 'Personal agreement',
  contract = 'Contract',
  photo = 'Self photos',
  other = 'Other'
}

In the component ts file:

export class MyBestComponent implements OnInit {
  fileCategory = FileCategory;

  // returns keys of enum
  fileKeys(): Array<string> {
    const keys = Object.keys(this.fileCategory);
    return keys;
  }

  // returns values of enum
  fileVals(): Array<string> {
    const keys = Object.keys(this.fileCategory);
    return keys.map(el => Object(this.fileCategory)[el]);
  }

In the HTML template display these enum's values and keys:

  <a *ngFor="let cat of fileVals()"
     (click)="addFileCategory(cat)">{{cat}}</a>
  <a *ngFor="let cat of fileKeys()"
     (click)="addFileCategory(cat)">{{cat}}</a>

Solution 9 - Angular

I recommend you to use a generic Pipe, it will be more flexible and less redundant in your code. The problem with some previous propositions is that the typescript allow you to have different kind of enum, not only number/string.

For example:

export enum NotificationGrouping {
    GroupByCreatedAt = "GroupByCreatedAt", 
    GroupByCreatedByUser = "GroupByCreatedByUser", 
    GroupByEntity = "GroupByEntity", 
    GroupByAction = "GroupByAction", 
}

Here is my solution:

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

@Pipe({
	name: 'enumToArray'
})
export class EnumToArrayPipe implements PipeTransform {

  transform(value, args: string[]): any {
    let result = [];
    var keys = Object.keys(value);
    var values = Object.values(value);
    for (var i = 0; i < keys.length; i++) {
      result.push({ key: keys[i], value: values[i] });
    }
	return result; 
    //or if you want to order the result: 
    //return result.sort((a, b) => a.value < b.value ? -1 : 1);
  }
}

and the html will be:

<mat-select [(ngModel)]="groupKey">
  <mat-option *ngFor="let group of notificationGrouping | enumToArray"
              [value]="group.key">
    {{ group.value }}
  </mat-option>
</mat-select>

in ts:

public notificationGrouping : NotificationGrouping

Note: Still interesting to see people putting a minus without explanation ... For others who could be interested by this solution, I can confirm that it works correctly.

Solution 10 - Angular

ES6 supports

export enum E {
    a = 'First',
    b = 'Second',
    c = 'Third'
}

let keyValueArray = Object.keys(E).map(k => ({key: k, value: E[k as any]}));

Solution 11 - Angular

In Angular 7, still getting a list of all keys and values when using keys().

Based on the above answers I am using this for a simple ENUM, seems cleaner and more OO:

export enum CategoryType {
    Type1,
    Type2,
    ...,
}

export namespace CategoryType {
    export function keys() {
        return Object.keys(CategoryType).filter(k => !isNaN(Number(k)));
    }
}

then in the template:

<option *ngFor="let type of types.keys()" [value]="type">{{types[type]}}</option>

The function becomes another entry in the enum, but gets filtered out like the other non-numbers.

Solution 12 - Angular

 fillKeysValueFromEnum<T>(type:T){
    return Object.keys(type).filter(t => isNaN(+t)).map(el => {
       return {
         key: el,
         value: Object(type)[el]
       }
     });
  }

Then

fillKeysValueFromEnum(ENUM_HERE)

Solution 13 - Angular

I am very late to it and my answer might not directly solve the question but it gets the job done. Today I came across a ask which revolved around the same problem, i.e., iterating enum.

I took to creating a object instead of using enum.

export const Roles= {
    0: "ServiceAdmin", 1: "CompanyAdmin", 2: "Foreman", 3: "AgentForeman", 
    4: "CrewMember", 5: "AgentCrewMember", 6: "Customer"
}

To iterate use like below:

let Number=Number; 
let roles= Roles; // in .ts file
<select>
    <option *ngFor="let role of roles | keyvalue" [value]="Number({{role.key}})">
        {{role.value}}
    </option>
</select>

To parse a value use like below:

let selectedRoleValue= 4; 
let Roles= Roles; // in .ts file

<div>{{Roles[selectedRoleValue]}}</div>

This will display

> CrewMember

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRob GormanView Question on Stackoverflow
Solution 1 - AngularMurolackView Answer on Stackoverflow
Solution 2 - AngularyurzuiView Answer on Stackoverflow
Solution 3 - AngularGünter ZöchbauerView Answer on Stackoverflow
Solution 4 - AngularFilipe Morais JorgeView Answer on Stackoverflow
Solution 5 - AngularRob GormanView Answer on Stackoverflow
Solution 6 - AngularNeo_RyuView Answer on Stackoverflow
Solution 7 - AngularEladTalView Answer on Stackoverflow
Solution 8 - AngularPax BeachView Answer on Stackoverflow
Solution 9 - AngularCedric ArnouldView Answer on Stackoverflow
Solution 10 - AngularSameera R.View Answer on Stackoverflow
Solution 11 - AngularDovev HefetzView Answer on Stackoverflow
Solution 12 - AngularHoopouView Answer on Stackoverflow
Solution 13 - AngularAbhishek TewariView Answer on Stackoverflow