在 Angular2中根据枚举选择

我有这个枚举(我使用的是 打字机) :

export enum CountryCodeEnum {
France = 1,
Belgium = 2
}

我想在我的 表格中构建一个 选择,对于每个 选择,枚举整数值作为值,枚举文本作为标签,如下所示:

<select>
<option value="1">France</option>
<option value="2">Belgium</option>
</select>

我怎么能这么做?

116160 次浏览

update2 simplified by creating an array

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

update

instead of pipes: [KeysPipe]

use

@NgModule({
declarations: [KeysPipe],
exports: [KeysPipe],
}
export class SharedModule{}
@NgModule({
...
imports: [SharedModule],
})

original

Using the keys pipe from https://stackoverflow.com/a/35536052/217408

I had to modify the pipe a bit to make it work properly with enums (see also How to get names of enum entries?)

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (var enumMember in value) {
if (!isNaN(parseInt(enumMember, 10))) {
keys.push({key: enumMember, value: value[enumMember]});
// Uncomment if you want log
// console.log("enum member: ", value[enumMember]);
}
}
return keys;
}
}


@Component({ ...
pipes: [KeysPipe],
template: `
<select>
<option *ngFor="let item of countries | keys" [value]="item.key">\{\{item.value}}</option>
</select>
`
})
class MyComponent {
countries = CountryCodeEnum;
}

Plunker

See also How to iterate object keys using *ngFor?

One more solution if you don't want to create a new pipe. You could also extract keys into helper property and use it:

@Component({
selector: 'my-app',
providers: [],
template: `
<div>
<select>
<option *ngFor="let key of keys" [value]="key" [label]="countries[key]"></option>
</select>
</div>
`,
directives: []
})
export class App {


countries = CountryCodeEnum


constructor() {
this.keys = Object.keys(this.countries).filter(k => !isNaN(Number(k)));
}
}

Demo: http://plnkr.co/edit/CMFt6Zl7lLYgnHoKKa4E?p=preview

Edit:

if you need the options as numbers instead of strings:

  • replace [value] with [ngValue]
  • add .map(Number) after .filter(...)

Here is a very straightforward way for Angular2 v2.0.0. For completeness sake, I have included an example of setting a default value of the country select via reactive forms.

@Component({
selector: 'my-app',
providers: [],
template: `
<div>
<select id="country" formControlName="country">
<option *ngFor="let key of keys" [value]="key">\{\{countries[key]}}</option>
</select>
</div>
`,
directives: []
})
export class App {
keys: any[];
countries = CountryCodeEnum;


constructor(private fb: FormBuilder) {
this.keys = Object.keys(this.countries).filter(Number);
this.country = CountryCodeEnum.Belgium; //Default the value
}
}

I've preferred to have a simple utility function shared across my Angular App, to convert the enum into a standard array to build selects:

export function enumSelector(definition) {
return Object.keys(definition)
.map(key => ({ value: definition[key], title: key }));
}

to fill a variable in the Component with:

public countries = enumSelector(CountryCodeEnum);

and then fill my Material Select as my old array based ones:

<md-select placeholder="Country" [(ngModel)]="country" name="country">
<md-option *ngFor="let c of countries" [value]="c.value">
\{\{ c.title }}
</md-option>
</md-select>

Thanks for this thread!

Another similar solution, that does not omit "0" (like "Unset"). Using filter(Number) IMHO is not a good approach.

@Component({
selector: 'my-app',
providers: [],
template: `
<select>
<option *ngFor="let key of keys" [value]="key" [label]="countries[key]"></option>
</select>`,
directives: []
})


export class App {
countries = CountryCodeEnum;


constructor() {
this.keys = Object.keys(this.countries).filter(f => !isNaN(Number(f)));
}
}


// ** NOTE: This enum contains 0 index **
export enum CountryCodeEnum {
Unset = 0,
US = 1,
EU = 2
}

With string enums you can try this.

My string enum has the following definition:

    enum StatusEnum {
Published = <any> 'published',
Draft = <any> 'draft'
}

and translates to js in the following way:

   {
Published: "published",
published: "Published",
Draft: "draft",
draft: "Draft"
}

I have a few of these in my project so created small helper function in a shared service lib:

   @Injectable()
export class UtilsService {
stringEnumToKeyValue(stringEnum) {
const keyValue = [];
const keys = Object.keys(stringEnum).filter((value, index) => {
return !(index % 2);
});


for (const k of keys) {
keyValue.push({key: k, value: stringEnum[k]});
}


return keyValue;
}
}

Init in your component constructor and Bind it to your template like this:

In component:

    statusSelect;


constructor(private utils: UtilsService) {
this.statusSelect = this.utils.stringEnumToKeyValue(StatusEnum);
}

In template:

    <option *ngFor="let status of statusSelect" [value]="status.value">
\{\{status.key}}
</option>

Don't forget to add the UtilsService to the provider array in your app.module.ts so you can easily inject it in different components.

I'm a typescript newbie so please correct me if I'm wrong or if there are better solutions.

Another spin off of this answer, but this actually maps the values as numbers, instead of converting them to strings which is a bug. It also works with 0 based enums

@Component({
selector: 'my-app',
providers: [],
template: `
<select>
<option *ngFor="let key of keys" [value]="key" [label]="countries[key]"></option>
</select>`,
directives: []
})


export class App {
countries = CountryCodeEnum;


constructor() {
this.keys = Object.keys(this.countries)
.filter(f => !isNaN(Number(f)))
.map(k => parseInt(k));;
}
}

As of Angular 6.1 and above you can use the built-in KeyValuePipe like below (pasted from angular.io docs).

I'm assuming that an enum contains human friendly readable strings of course :)

@Component({
selector: 'keyvalue-pipe',
template: `<span>
<p>Object</p>
<div *ngFor="let item of object | keyvalue">
\{\{item.key}}:\{\{item.value}}
</div>
<p>Map</p>
<div *ngFor="let item of map | keyvalue">
\{\{item.key}}:\{\{item.value}}
</div>
</span>`
})
export class KeyValuePipeComponent {
object: {[key: number]: string} = {2: 'foo', 1: 'bar'};
map = new Map([[2, 'foo'], [1, 'bar']]);
}

This is the best option which you can apply without any pipes or extra code.

import { Component } from '@angular/core';


enum AgentStatus {
available =1 ,
busy = 2,
away = 3,
offline = 0
}




@Component({
selector: 'my-app',
template: `
<h1>Choose Value</h1>


<select (change)="parseValue($event.target.value)">
<option>--select--</option>
<option *ngFor="let name of options"
[value]="name">\{\{name}}</option>
</select>


<h1 [hidden]="myValue == null">
You entered \{\{AgentStatus[myValue]}}


</h1>`
})
export class AppComponent {




options : string[];
myValue: AgentStatus;
AgentStatus : typeof AgentStatus = AgentStatus;


ngOnInit() {
var x = AgentStatus;
var options = Object.keys(AgentStatus);
this.options = options.slice(options.length / 2);
}


parseValue(value : string) {
this.myValue = AgentStatus[value];


}
}

Yet another Solution with Angular 6.1.10 / Typescript ...

 enum Test {
No,
Pipe,
Needed,
Just,
Use,
Filter
}


console.log('Labels: ');
let i = 0;
const selectOptions = [
];
Object.keys(Test).filter(key => !Number(key) && key !== '0').forEach(key => {
selectOptions.push({position: i, text: key});
i++;
});
console.log(selectOptions);

This will print:

Console:
Labels:
(6) [{…}, {…}, {…}, {…}, {…}, {…}]
0: {position: 0, text: "No"}
1: {position: 1, text: "Pipe"}
2: {position: 2, text: "Needed"}
3: {position: 3, text: "Just"}
4: {position: 4, text: "Use"}
5: {position: 5, text: "Filter"}
export enum Unit
{
Kg = 1,
Pack,
Piece,
Litre
}

//with map

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


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


transform(enumObj: Object) {


const keys = Object.keys(enumObj).filter(key => parseInt(key));
let map = new Map<string, string>();
keys.forEach(key => map.set(key, enumObj[key]))
console.log( Array.from(map));
return Array.from(map);
}


}

//With set

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


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


transform(enumObj: Object) {


const keys = Object.keys(enumObj).filter(key => parseInt(key));
let set = new Set();
keys.forEach(key => set.add({ key: parseInt(key), value: enumObj[key] }))
return Array.from(set);
}


}