Home » Angularjs » Correct way to utilize Typescript enum in angular HTML pages (e.g. angular ng-class)

Correct way to utilize Typescript enum in angular HTML pages (e.g. angular ng-class)

Posted by: admin November 30, 2017 Leave a comment

Questions:

New to angular and typescript.

I have typescript enum as follows

public enum MyEnum{
   A = 0,
   B = 1,
   C = 2
}

A scope variable as-

$scope.myLetter: MyEnum = MyEnum.B;

What is the correct way to put the enum check?

Option 1: Compare the integer value of enum in html page-

<div ng-class="{classA: myLetter === 0, classB: myLetter === 1, classC: myLetter === 2}">Test panel</div>

Option 2: Get the class name from the controller scope method

$scope.getClass(value: myLetter): string{
    if(value === MyEnum.A)
    return 'classA';

    if(value === MyEnum.B)
    return 'classB';

    if(value === MyEnum.C)
    return 'classC';
}

And to have html element as-

<div ng-class='getClass(myLetter)'>Test panel</div>

Option 3: answer given by ‘RyanNerd’ at Angular.js and ng-switch-when – emulating enum

For me option 2 is preferable, remaining options have checks in ng-class value as string, which will not give us static type enforcement. Please share your views or any other better option if you have.

Answers:

Get the class name from the controller scope method

I do not like an idea of making controller to know class names.

  1. You can add converter function to the scope:

    $scope.myEnumName = (value: MyEnum) => MyEnum[value];
    

    and use it in template:

    ng-class="{'A':'ClassA', 'B':'ClassB', 'C':'ClassC'}[myEnumName(myLetter)]"
    
  2. Or add switch function

    $scope.switchMyEnum =
        <T>(value: MyEnum, cases: { [value: string]: T }) => cases[MyEnum[value]];
    

    template:

    ng-class="switchMyEnum(myLetter, {'A':'ClassA', 'B':'ClassB', 'C':'ClassC'})
    
  3. If you need only myLetter switch:

    $scope.switchMyLetter =
        <T>(cases: { [value: string]: T }) => cases[MyEnum[$scope.myLetter]];
    

    template:

    ng-class="switchMyLetter({'A':'ClassA', 'B':'ClassB', 'C':'ClassC'})
    
  4. If you want to use a number of enums in many scopes:

    angular.module("MyApp", [])
      .run(["$rootScope", (root: {}) => {
        function registerSwitchers(...enumInfos: [string, { [value: number]: string }][]) {
          enumInfos.forEach(enumInfo => {
            var switcherName = enumInfo[0]
            var enumType = enumInfo[1]
            root[switcherName] = (value: any, cases: { [value: string]: any }) => cases[enumType[value]];
          });
        }
        registerSwitchers(
          ["switchMyEnum1", MyEnum1],
          ["switchMyEnum2", MyEnum2]);
      }])
    
Questions:
Answers:

You could also build the class object in your controller and set it as expression (with bracket notation) in your view.

Example:-

$scope.panelClass = {};
$scope.panelClass[MyEnum.A] = 'classA';
$scope.panelClass[MyEnum.B] = 'classB';
$scope.panelClass[MyEnum.C] = 'classC';

You can write the above as shorthand syntax (ES6), provided your typescript version supports (has polyfill support) it, so you can rewrite as:

$scope.panelClass = {
    [MyEnum.A]:'classA',
    [MyEnum.B]:'classB', 
    [MyEnum.C]:'classC'
};

and use it as:

<div ng-class="panelClass[myLetter]">Test panel</div>

This is similar to the shorthand ng-class expression:

<div ng-class="{0:'classA', 1:'classB', 2:'classC'}[myLetter]">Test panel</div>

Questions:
Answers:

We usually need Enums when we need number values to be referred explicitly. In the above use case I don’t see the explicit use case of using enums. An array would just work fine, as demonstrated:

((): void => {

 var ClassConstant: string[] = ['classA', 'classB', 'classC'];
 
 angular
  .module('app', [])
  .constant('ClassConstant', ClassConstant)
  .controller('AppController', ($scope, ClassConstant) => {
	  $scope.setClass = (classname: string) => {
		return ClassConstant[classname];
	  };
  });
 })();
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body ng-app="app">

  <div ng-controller="AppController">
    <div ng-class="{{setClass(myLetter)}}">1</div>
  </div>
  
</body>
</html>

Questions:
Answers:

I has been confronted too to this question.

This is my solution :

I create a function in controller who return a boolean for each value of my enum.

In my controller.ts

export class AController { 
    public TestEnumA(): boolean { 
        return this.scope.field == MyEnum.A; 
    }

    public TestEnumB(): boolean { 
        return this.scope.field == MyEnum.B; 
    }
}

In my view.html

<div ngController="AController as controllerAlias">
    <div class="a-class" ng-class="{'classA-true': controllerAlias.TestEnumA(), 'classB-true': controllerAlias.TestEnumB()}"><div>
</div>

Why I have choose this solution ?

This way, I don’t hard code the css class in the controller (hard coded the css class in your controller it is not a good idea to ensure the maintability of the view)

This way, I don’t hard code the enum value in your ng-class;

This way, I still benefits of auto code completion AND the verification at the compilation.

Questions:
Answers:

You can set your enum in the rootScope

angular
    .module('moduleName', [])
    .run(['$rootScope', function ($rootScope) {
        $rootScope.Enum = PathToEnum.Enum;
     }]);
  • pro: It is easy to set up and you can use it everywhere (in differents controllers or in view)
  • cons: you lose the autcompletion and the verification at the compilation

In View

<div ngController="AController">
    <div class="class" ng-class="{$root.Enum.A: 'classA', $root.Enum.B: 'classB'}[valueInScope]">
    </div>
</div>

Leave a Reply

Your email address will not be published. Required fields are marked *