How to use TypeScript Enum types, especially with Angular 2

Let’s assume you have the following enum in TypeScript:

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

An 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. Let me explain…

A very common thing you might want to do, is convert a string to an enum. 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 do you convert a string to an enum in TypeScript?

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 to convert a TypeScript enum value to a string?

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

How to get all the values of a TypeScript enum type?

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: Undefined enum type in Angular 2 views

If you write this in your Angular2 template:

{{AgentStatus[myValue]}}

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

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 With All Solutions

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

(Click the button at the top right to see it in action)

This post was inspired by this StackOverflow answer.

Solving Common Angular 2 HTTP Pitfalls: No map() Method for respose.json() & No Http Provider

I had so much fun helping mentoring a couple dozen developers yesterday in SSW’s Angular Hack Day here in Sydney. It was an awesome day from organizers to students.

This post is about the Number 1 problem all students seemed to have, and how to solve it.

The Most Common Problem: map Not A Function

When you make an HTTP request to a JSON endpoint, you map the response text to a JSON object, like this:

getList() : Observable<ListItem[]> {
  return this.http.get(this._listUrl)
                 .map(response => response.json())
                 .catch(this.handleError);
}

When the students ran their own code, which more or less looked like the segment here, they got an exception like this:

angular2.dev.js:23925 EXCEPTION: TypeError: this._http.get(...).map is not a function in [null]

2016-04-24_13-31-23

The reason for this is that the result of the HTTP call is an Observable. An Observable has nothing defined by default except subscribe. You need to import any other operator manually like

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

If you rely on autocomplete in your editor, and it shows a couple of versions for every operator, remember to choose the with with “/add/” in it. As this is the file that add the operator to the Observable definition.

You cannot add * unfortunately.
Update: You can import all RxJS operators in one call like this:

import 'rxjs/add/operator/';

Single Include

You can also move the import(s) from every TypeScript file to the main entry point of your app, likely the file with the bootstrap() call.

Depending on how you set up your compilation and module loader, the entry file import might not work. It works with the SystemJS use you see it in the official Angular 2 quick start though.

Obviously, if you think the entry file include is a hacky way, just add the imports on top of the each file that uses them.

Update for gulp-typescript Users

I found a case where a user on StackOverflow had the same problem, even though he had the correct imports in the bootstrap file. The problem was after upgrading to Angular 2 beta 17.

In this particular case, the user reported that he was using gulp-typescript version 2.12.0. He reported that the problem went away by just upgrading to the very next version, gulp-typescript version 2.13.0.

Oh, and make sure you are using TypeScript 1.8+ also to be safe.

Another Problem: No Providers for Http

Some students also were getting a different error:

angular2.dev.js:23925 EXCEPTION: Error: Uncaught (in promise): No provider for Http! (CustomersComponent -> DataService -> Http)

2016-04-24_13-52-18

The forgotten part this time was adding the Http providers to the bootstrap. Something like this:

import { bootstrap }    from '@angular/platform-browser-dynamic';
import { HTTP_PROVIDERS } from '@angular/http';
import { AppComponent } from './app.component';

bootstrap(AppComponent, [HTTP_PROVIDERS]);

One last tip: Make sure you included the Http file in your scripts if you are starting from a quickstart start or so. As the Http module is included in a separate file in Angular 2.

A Quick Shoutout to Dan Wahlin

During the hack day, students asked me for a good example that shows the solution above. It was not in the official examples (the 5 minutes quickstart, and the Tour of Heroes).

The best I have found on Github is Dan Wahlin‘s Angular 2 JumpStart sample.

It’s named similarly to his Udemy course for Angular 1. I remember his AngularJS in 60-ish minutes YouTube video was a key block in building my Angular 1 learning when I first started it back in 2013.

Thanks for everything, Dan :)

Different People, Different Challenges

With various people hacking away, I got to see different problems that people had getting up and running with Angular 2.

For some people, just setting up Node was more challenging than it should be.

Some others had issues with the sample APIs they chose, because they didn’t have cross domain support (CORS), or returned XML by default and they needed to add an Accept header explicitly.

Some were wondering how to use RxJS to combine results form 2 separate HTTP requests (getting city weather from one, and information about it, or an image URL from another).

How About You?

All these variations got me pretty curious. What was the biggest blocker you had when trying to play with code in Angular 2?

What were your own challenges?

Tweet them to me on Twitter (just mention @Meligy), or just write it down in a comment below.

A Quick 10-Min Video To Start Writing Angular 2 With No TypeScript Setup

Hello everyone,

In this video, I share a very simple tip that I earlier shared with a few Newsletter subscribers and ng-sydney members, about the easiest way you can get to play with Angular2, without worrying about shims, SystemJS, TypeScript, or RxJS.

I also give you another hint for when you want to create a “proper” project, not just a playground.

Too Long; Didn’t Read (Watch)

  • To start a new Angular2 playground, go to angular.io, scroll down to the hellow world example, and click the “TRY IN PLUNKER” button
  • If you need a proper project, just google “Minko Angular2 Seed”, click on the Angular 2 Seed Github repository, and clone that (maybe with flag --depth 1 for clean history)

Let me know if you have any questions.