24

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.

5 Answers 5

9

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]);
      }])
    
Sign up to request clarification or add additional context in comments.

Comments

2

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>

Comments

1

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>

Comments

1

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.

Comments

1

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.