Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Don't know how to implement an enum using an interface in an angular2 class

Going around in circles here. Very new to Typescript and it's causing major headaches with trivial implementations.

How do a define in AgentStatusService that it should have an array of 4 options called ['offline','available','busy','away'] ? AgentStatus is defined ( or is it? ) and I am injecting it into the AgentStatusService.

Microsoft Visual Studio Code is barfing on line 21 where the type 'typeof AgentStatus' is not assignable to type 'AgentStatus'... why?

Updated:

import { EventEmitter, Injectable } from '@angular/core';

export enum AgentStatus {
    available =1 ,
    busy = 2,
    away = 3,
    offline = 0
}

export interface IAgentStatusService {
    state: number
    states: AgentStatus
}
@Injectable()
export class AgentStatusService implements IAgentStatusService {

    state:number;  // this really should be string, but line 22 returns a number
    states:AgentStatus;

    constructor(states:typeof AgentStatus = AgentStatus){
        // unreacheable code browser_adapter.ts:78EXCEPTION: Error: Uncaught (in promise): TypeError: Cannot read property 'isSkipSelf' of null
        // absolutely impossible to debug...
        this.state = AgentStatus.offline // this returns a number
    }

    //   set state(state:string){
    //       try{
    //       this._state = this.states[state];
    //       // string 


    //       } catch(e){
    //           console.log('tried setting bad enum value on AgentStatus', e.stack);
    //       }
    //   }

    //   get state():string{
    //       return this._state; 
    //   }

    //   get model():any {
    //     return this.states;    
    //   }
}

Compare with This implementation satisfies angular2:

@Injectable()
export class AgentStatusService {
    public states = ['offline','available','busy','away'];
    private _state;

    constructor(){
        this._state = this.states[0];
    }

    set state(state:string){
        try{
        this._state = this.states[state];
        } catch(e){
            console.log('tried setting bad enum value on AgentStatus', e.stack);
        }
    }

    get state():string{
        return this._state; 
    }

    get model():any {
        return this.states;    
    }
}
like image 870
Simon Avatar asked May 24 '16 00:05

Simon


2 Answers

It's not obvious what you want here, but let me explain a few things...

I have created a blog post talking about this:
How to use TypeScript Enums, especially with Angular 2+
But I'll include all the information inline here:

A enum is just an object. Your enum is written something like this in JavaScript:

{
    0: "offline",
    1: "available",
    2: "busy",
    3: "away",
    available: 1,
    busy: 2,
    away: 3,
    offline: 0
}

The benefit from typing is very limited in enums.

This line is valid:

var value = <AgentStatus>"offline";

But it's not useful, because

value == AgentStatus.offline // <- false, because it's "offline" == 0

So, you should always store your values as numbers, which you can obtain as follows:

How to convert string to enum

var value = AgentStatus["offline"]; // so value is now 0

// You can also use this, which only gives IDE hints, no runtime benefit
var value: AgentStatus = AgentStatus["offline"];

This makes the previous comparison work:

value == AgentStatus.offline // <- true, because it's 0 == 0

Now, a couple questions remain:

How do you get the string equivalent?

AgentStatus.offline // 0
AgentStatus[AgentStatus.offline] // -> AgentStatus[0] -> "offline"

How do you get all possible enum values?

var options : string[] = Object.keys(AgentStatus);
// The options list has the numeric keys, followed by the string keys
// So, the first half is numeric, the 2nd half is strings
options = options.slice(options.length / 2);

Gotcha

If you write this in your template:

{{AgentStatus[myValue]}}

It will fail, because it doesn't have access to imported types (it gets executed later by Angular).

To make it work, your component will need to have a reference to the enum type / object, something like:

export class MyComponent {
    // allows you to use AgentStatus in template
    AgentStatus = AgentStatus;        

    myValue : AgentStatus;
    // ...
}

Runnable Demo

Here is an example that explains everything I pointed in here:

http://plnkr.co/edit/vOeeDrCI6CmsXdWaMtwG?p=preview

Look in the file: app/app.component.ts.

like image 168
Meligy Avatar answered Oct 24 '22 23:10

Meligy


Microsoft Visual Studio Code is barfing on line 21 where the type 'typeof AgentStatus' is not assignable to type 'AgentStatus'... why

You have states:AgentStatus = AgentStatus. Here states:AgentStatus is actually a value of type AgentStatus where as = AngentStatus is the whole enum.

Fix

you probably want:

states:typeof AgentStatus = AgentStatus

Or

states:AgentStatus = AgentStatus.Available

More

This is similar to you try to assign foo:SomeClass = SomeClass instead of foo:SomeClass = new SomeClass().

like image 21
basarat Avatar answered Oct 24 '22 23:10

basarat