[typescript] How to use a typescript enum value in an Angular2 ngSwitch statement

The Typescript enum seems a natural match with Angular2's ngSwitch directive. But when I try to use an enum in my component's template, I get "Cannot read property 'xxx' of undefined in ...". How can I use enum values in my component template?

Please note that this is different from how to create html select options based upon ALL of the values of an enum (ngFor). This question is about ngSwitch based upon a particular value of an enum. Although the same approach of creating an class-internal reference to the enum appears.

This question is related to typescript angular

The answer is


as of rc.6 / final

...

export enum AdnetNetworkPropSelector {
    CONTENT,
    PACKAGE,
    RESOURCE
}

<div style="height: 100%">
          <div [ngSwitch]="propSelector">
                 <div *ngSwitchCase="adnetNetworkPropSelector.CONTENT">
                      <AdnetNetworkPackageContentProps [setAdnetContentModels]="adnetNetworkPackageContent.selectedAdnetContentModel">
                                    </AdnetNetworkPackageContentProps>
                  </div>
                 <div *ngSwitchCase="adnetNetworkPropSelector.PACKAGE">
                </div>
            </div>              
        </div>


export class AdnetNetwork {       
    private adnetNetworkPropSelector = AdnetNetworkPropSelector;
    private propSelector = AdnetNetworkPropSelector.CONTENT;
}

As an alternative to @Eric Lease's decorator, which unfortunately doesn't work using --aot (and thus --prod) builds, I resorted to using a service which exposes all my application's enums. Just need to publicly inject that into each component which requires it, under an easy name, after which you can access the enums in your views. E.g.:

Service

import { Injectable } from '@angular/core';
import { MyEnumType } from './app.enums';

@Injectable()
export class EnumsService {
  MyEnumType = MyEnumType;
  // ...
}

Don't forget to include it in your module's provider list.

Component class

export class MyComponent {
  constructor(public enums: EnumsService) {}
  @Input() public someProperty: MyEnumType;

  // ...
}

Component html

<div *ngIf="someProperty === enums.MyEnumType.SomeValue">Match!</div>

This's simple and works like a charm :) just declare your enum like this and you can use it on HTML template

  statusEnum: typeof StatusEnum = StatusEnum;

Start by considering 'Do I really want to do this?'

I have no problem referring to enums directly in HTML, but in some cases there are cleaner alternatives that don't lose type-safe-ness. For instance if you choose the approach shown in my other answer, you may have declared TT in your component something like this:

public TT = 
{
    // Enum defines (Horizontal | Vertical)
    FeatureBoxResponsiveLayout: FeatureBoxResponsiveLayout   
}

To show a different layout in your HTML, you'd have an *ngIf for each layout type, and you could refer directly to the enum in your component's HTML:

*ngIf="(featureBoxResponsiveService.layout | async) == TT.FeatureBoxResponsiveLayout.Horizontal"

This example uses a service to get the current layout, runs it through the async pipe and then compares it to our enum value. It's pretty verbose, convoluted and not much fun to look at. It also exposes the name of the enum, which itself may be overly verbose.

Alternative, that retains type safety from the HTML

Alternatively you can do the following, and declare a more readable function in your component's .ts file :

*ngIf="isResponsiveLayout('Horizontal')"

Much cleaner! But what if someone types in 'Horziontal' by mistake? The whole reason you wanted to use an enum in the HTML was to be typesafe right?

We can still achieve that with keyof and some typescript magic. This is the definition of the function:

isResponsiveLayout(value: keyof typeof FeatureBoxResponsiveLayout)
{
    return FeatureBoxResponsiveLayout[value] == this.featureBoxResponsiveService.layout.value;
}

Note the usage of FeatureBoxResponsiveLayout[string] which converts the string value passed in to the numeric value of the enum.

This will give an error message with an AOT compilation if you use an invalid value.

Argument of type '"H4orizontal"' is not assignable to parameter of type '"Vertical" | "Horizontal"

Currently VSCode isn't smart enough to underline H4orizontal in the HTML editor, but you'll get the warning at compile time (with --prod build or --aot switch). This also may be improved upon in a future update.


If using the 'typetable reference' approach (from @Carl G) and you're using multiple type tables you might want to consider this way :

export default class AppComponent {

  // Store a reference to the enums (must be public for --AOT to work)
  public TT = { 
       CellType: CellType, 
       CatType: CatType, 
       DogType: DogType 
  };

  ...

  dog = DogType.GoldenRetriever; 

Then access in your html file with

{{ TT.DogType[dog] }}   => "GoldenRetriever"

I favor this approach as it makes it clear you're referring to a typetable, and also avoids unnecessary pollution of your component file.

You can also put a global TT somewhere and add enums to it as needed (if you want this you may as well make a service as shown by @VincentSels answer). If you have many many typetables this may become cumbersome.

Also you always rename them in your declaration to get a shorter name.


You can create a custom decorator to add to your component to make it aware of enums.

myenum.enum.ts:

export enum MyEnum {
    FirstValue,
    SecondValue
}

myenumaware.decorator.ts

import { MyEnum } from './myenum.enum';

export function MyEnumAware(constructor: Function) {
    constructor.prototype.MyEnum = MyEnum;
}

enum-aware.component.ts

import { Component } from '@angular2/core';
import { MyEnum } from './myenum.enum';
import { MyEnumAware } from './myenumaware.decorator';

@Component({
  selector: 'enum-aware',
  template: `
    <div [ngSwitch]="myEnumValue">
      <div *ngSwitchCase="MyEnum.FirstValue">
        First Value
      </div>
      <div *ngSwitchCase="MyEnum.SecondValue">
        Second Value
      </div>
    </div>
    <button (click)="toggleValue()">Toggle Value</button>
  `,
})
@MyEnumAware // <---------------!!!
export default class EnumAwareComponent {
  myEnumValue: MyEnum = MyEnum.FirstValue;

  toggleValue() {
    this.myEnumValue = this.myEnumValue === MyEnum.FirstValue
        ? MyEnum.SecondValue : MyEnum.FirstValue;
  }
}

My component used an object myClassObject of type MyClass, which itself was using MyEnum. This lead to the same issue described above. Solved it by doing:

export enum MyEnum {
    Option1,
    Option2,
    Option3
}
export class MyClass {
    myEnum: typeof MyEnum;
    myEnumField: MyEnum;
    someOtherField: string;
}

and then using this in the template as

<div [ngSwitch]="myClassObject.myEnumField">
  <div *ngSwitchCase="myClassObject.myEnum.Option1">
    Do something for Option1
  </div>
  <div *ngSwitchCase="myClassObject.myEnum.Option2">
    Do something for Option2
  </div>
  <div *ngSwitchCase="myClassObject.myEnum.Option3">
    Do something for Opiton3
  </div>
</div>

Angular4 - Using Enum in HTML Template ngSwitch / ngSwitchCase

Solution here: https://stackoverflow.com/a/42464835/802196

credit: @snorkpete

In your component, you have

enum MyEnum{
  First,
  Second
}

Then in your component, you bring in the Enum type via a member 'MyEnum', and create another member for your enum variable 'myEnumVar' :

export class MyComponent{
  MyEnum = MyEnum;
  myEnumVar:MyEnum = MyEnum.Second
  ...
}

You can now use myEnumVar and MyEnum in your .html template. Eg, Using Enums in ngSwitch:

<div [ngSwitch]="myEnumVar">
  <div *ngSwitchCase="MyEnum.First"><app-first-component></app-first-component></div>
  <div *ngSwitchCase="MyEnum.Second"><app-second-component></app-second-component></div>
  <div *ngSwitchDefault>MyEnumVar {{myEnumVar}} is not handled.</div>
</div>

Examples related to typescript

TS1086: An accessor cannot be declared in ambient context Element implicitly has an 'any' type because expression of type 'string' can't be used to index Angular @ViewChild() error: Expected 2 arguments, but got 1 Typescript: No index signature with a parameter of type 'string' was found on type '{ "A": string; } Understanding esModuleInterop in tsconfig file How can I solve the error 'TS2532: Object is possibly 'undefined'? Typescript: Type 'string | undefined' is not assignable to type 'string' Typescript: Type X is missing the following properties from type Y length, pop, push, concat, and 26 more. [2740] Can't perform a React state update on an unmounted component TypeScript and React - children type?

Examples related to angular

error NG6002: Appears in the NgModule.imports of AppModule, but could not be resolved to an NgModule class error TS1086: An accessor cannot be declared in an ambient context in Angular 9 TS1086: An accessor cannot be declared in ambient context @angular/material/index.d.ts' is not a module Why powershell does not run Angular commands? error: This is probably not a problem with npm. There is likely additional logging output above Angular @ViewChild() error: Expected 2 arguments, but got 1 Schema validation failed with the following errors: Data path ".builders['app-shell']" should have required property 'class' Access blocked by CORS policy: Response to preflight request doesn't pass access control check origin 'http://localhost:4200' has been blocked by CORS policy in Angular7