177

Can we use enums in an angular2 view template?

<div class="Dropdown" dropdownType="instrument"></div>

passes the string as input:

enum DropdownType {
    instrument,
    account,
    currency
}

@Component({
    selector: '[.Dropdown]',
})
export class Dropdown {

    @Input() public set dropdownType(value: any) {

        console.log(value);
    };
}

But how to pass an enum configuration? I want something like this in the template:

<div class="Dropdown" dropdownType="DropdownType.instrument"></div>

What would be the best practice?

Edited: Created an example:

import {bootstrap} from 'angular2/platform/browser';
import {Component, View, Input} from 'angular2/core';

export enum DropdownType {

    instrument = 0,
    account = 1,
    currency = 2
}

@Component({selector: '[.Dropdown]',})
@View({template: ''})
export class Dropdown {

    public dropdownTypes = DropdownType;

    @Input() public set dropdownType(value: any) {console.log(`-- dropdownType: ${value}`);};
    constructor() {console.log('-- Dropdown ready --');}
}

@Component({ selector: 'header' })
@View({ template: '<div class="Dropdown" dropdownType="dropdownTypes.instrument"> </div>', directives: [Dropdown] })
class Header {}

@Component({ selector: 'my-app' })
@View({ template: '<header></header>', directives: [Header] })
class Tester {}

bootstrap(Tester);
1

5 Answers 5

218

Create an Enum:

enum ACTIVE_OPTIONS {
  HOME = 0,
  USERS = 1,
  PLAYERS = 2
}

Create a component, be sure your enum list will have the typeof:

export class AppComponent {
  ACTIVE_OPTIONS = ACTIVE_OPTIONS;
  active: ACTIVE_OPTIONS;
}

Create a template:

<li [ngClass]="{ 'active': active === ACTIVE_OPTIONS.HOME }">
  <a routerLink="/in">
    <i class="fa fa-fw fa-dashboard"></i> Home
  </a>
</li>
7
  • 2
    Not a specialist myself, so I really have to question: is this solution always better than David L.'s? This one takes less lines of code, but in terms of memory usage it may be creating one list per instance of the host component class... And if this is true (not saying it is!), there's not much of a problem when dealing with AppComponent, but the solution might not be the best in the case of a CustomerComponent or something more recurrent. Am I right? Commented Jan 26, 2017 at 13:46
  • 2
    You could update the html as: [class.active]="active === ACTIVE_OPTIONS.HOME"
    – Neil
    Commented Mar 15, 2018 at 17:24
  • 2
    Aditya, it is better for the simple reason, that it involves one class, not 2. I don't have a parent class, and not going to create it for that reason :) Commented Jan 18, 2019 at 19:03
  • 1
    @YuriGridin Other than the unnecessary Parent class, the principle involved is the same. One just has an implicit type and the other an explicit type. Also, keep in mind this won't work for const enum types because they aren't created as classes, and their type is erased when transpiled to JavaScript.
    – crush
    Commented Feb 26, 2019 at 20:07
  • @crush - Agreed Commented Feb 27, 2019 at 21:45
165

Create a property for your enum on the parent component to your component class and assign the enum to it, then reference that property in your template.

export class Parent {
    public dropdownTypes = DropdownType;        
}

export class Dropdown {       
    @Input() public set dropdownType(value: any) {
        console.log(value);
    };
}

This allows you to enumerate the enum as expected in your template.

<div class="Dropdown" [dropdownType]="dropdownTypes.instrument"></div>
5
  • 2
    Based on your update, move your enum property declaration to the parent component.
    – David L
    Commented Mar 10, 2016 at 19:48
  • Oh, sure, gets from its context.
    – McLac
    Commented Mar 10, 2016 at 21:27
  • 1
    In case anyone is struggling to get that to work, note that it's "set dropdownType()", not "setDropDownType()" in the code above. It took me a while to see that. It also work with a member variable though.
    – murrayc
    Commented Sep 21, 2017 at 13:26
  • 2
    Pretty sure dropdownType in the template should have square brackets on both ends (like so: [dropdownType]) since it takes a var and not text.
    – Tom
    Commented Dec 27, 2018 at 10:03
  • Seems to work for me a little simpler, with just public dropdownTypes = DropdownType; in the child component, and nothing needing to get passed in from the parent Commented Jun 2, 2020 at 15:10
42

If you want get Enum name :

export enum Gender {
  Man = 1,
  Woman = 2
}

then in component file

public gender: typeof Gender = Gender;

in template

<input [value]="gender.Man" />
1
  • 1
    With this solution, the enum can be declared anywhere (e.g. some shared module), imported and exposed in the component (where it's used, even private suffices) and directly addressed in the component's template. It doesn't involve the parent component. Therefore I like it better.
    – twigmac
    Commented Feb 11, 2022 at 15:09
2

Maybe you don't have to do this.

For example, in Numeric Enum:

export enum DropdownType {
    instrument = 0,
    account = 1,
    currency = 2
}

In HTML Template:

<div class="Dropdown" [dropdownType]="1"></div>

result: dropdownType == DropdownType.account

or String Enum:

export enum DropdownType {
    instrument = "instrument",
    account = "account",
    currency = "currency"
}
<div class="Dropdown" [dropdownType]="'currency'"></div>

result: dropdownType == DropdownType.currency


If you want get Enum name :

val enumValue = DropdownType.currency
DropdownType[enumValue] //  print "currency", Even the "numeric enum" is also. 
2
  • 3
    Lets say I dont give the enum any value, if I change the enum order the HTML will be wrong. I think this is not a good approach Commented Jan 2, 2020 at 20:27
  • 1
    This will break when strictTemplates are enabled. Do not use this if you ever plan on enabling this setting. Commented Jun 1, 2021 at 10:10
0

I'm a bit late to the party.

You can use ...set (value: keyof DropdownType)...

That way the function can only be called with a key from the enum.

After that you just have to convert the string value to the enum value. This is not directly possible in TypeScript, but we can use a workaround:

An enum in TS will be compiled to

var DropdownType = {
    instrument = 0,
    account = 1,
    currency = 2
};

Object.freeze(DropdownType);

Since after the compilation the enum becomes an immutable object, we can use the name of the key as indexer. We just have to work around the compiler:

@Input() public set dropdownType(value: keyof DropdownType) {
    const enumValue = (DropdownType as any)[value] as DropdownType;
    console.log(enumValue);
};

Beware though, if an enum type gets added to JS this will probably not work anymore. And since we are working around the compiler, it will not warn you about any error that occurs.

Furthermore will Autocompletion only work if your IDE is smart enough for that. My VSCode Workspace does not Autocomplete, but WebStorm does.

Edit:

If you use enums more frequently this way, I would suggest creating a static helper function somewhere in the project:

function getValueFromEnum<T extends {[key: string]: string | number}>(
    e: T, 
    key: keyof T
) {
    return e[key];
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.