Reputation: 3694
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
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
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
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
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
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
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
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
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
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);
}
}
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
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
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
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
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