176

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);
Moo
  • 3,369
  • 4
  • 22
  • 41
McLac
  • 2,713
  • 3
  • 15
  • 19
  • 3
    Better than both of the answers below, though similar but simpler than the accepted one, is: https://stackoverflow.com/a/42464835/358578 – pbristow Jul 12 '18 at 17:51

5 Answers5

214

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>
developer033
  • 24,267
  • 8
  • 82
  • 108
Oswaldo Alvarez
  • 4,772
  • 1
  • 22
  • 20
  • 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? – Rui Pimentel Jan 26 '17 at 13:46
  • 2
    You could update the html as: [class.active]="active === ACTIVE_OPTIONS.HOME" – Neil Mar 15 '18 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 :) – Yuri Gridin Jan 18 '19 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 Feb 26 '19 at 20:07
  • @crush - Agreed – Yuri Gridin Feb 27 '19 at 21:45
  • Thanks! I actually used the same technique for [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator). I wanted to pass `TS enum` into `Vue component template`. – Alexey Vol May 01 '19 at 08:51
  • Thanks a lot for your solution. I would just add the typeof in your example : myEnumRef: typeof MyEnumRef = MyEnumRef; Looking for the way to do the same things witch constants, it works the same way : very useful ! – Benoît Plâtre Jan 28 '21 at 18:00
164

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>
Moo
  • 3,369
  • 4
  • 22
  • 41
David L
  • 32,885
  • 8
  • 62
  • 93
  • 2
    Based on your update, move your enum property declaration to the parent component. – David L Mar 10 '16 at 19:48
  • Oh, sure, gets from its context. – McLac Mar 10 '16 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 Sep 21 '17 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 Dec 27 '18 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 – Blair Connolly Jun 02 '20 at 15:10
41

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" />
Wilt
  • 41,477
  • 12
  • 152
  • 203
Milad Jafari
  • 970
  • 10
  • 15
  • 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 Feb 11 '22 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. 
zbin
  • 332
  • 2
  • 7
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];
}
deelk00
  • 1
  • 2