Rob Gorman
Rob Gorman

Reputation: 3694

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

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?

Upvotes: 145

Views: 137693

Answers (13)

Abhishek Tewari
Abhishek Tewari

Reputation: 425

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

Upvotes: 1

Hoopou
Hoopou

Reputation: 71

 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)

Upvotes: 1

Murolack
Murolack

Reputation: 2177

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

Upvotes: 175

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 658067

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

Upvotes: 17

Sameera R.
Sameera R.

Reputation: 4592

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

Upvotes: 5

EladTal
EladTal

Reputation: 2856

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>

Upvotes: 5

Cedric Arnould
Cedric Arnould

Reputation: 2433

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.

Upvotes: 2

Dovev Hefetz
Dovev Hefetz

Reputation: 1466

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.

Upvotes: 1

yurzui
yurzui

Reputation: 214305

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.

Upvotes: 119

Neo_Ryu
Neo_Ryu

Reputation: 121

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

Upvotes: 7

Pax Beach
Pax Beach

Reputation: 2813

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>

Upvotes: 3

Filipe Morais Jorge
Filipe Morais Jorge

Reputation: 116

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:
TypeScript: Add functions to an Enum https://basarat.gitbooks.io/typescript/content/docs/enums.html (at the end of the enums chapter.)

Upvotes: 9

Rob Gorman
Rob Gorman

Reputation: 3694

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.

Upvotes: 9

Related Questions