Angular 2 for Angular 1 Developers — Full Video of My DDD Sydney 2016 Talk

Whether you have been to my talk at DDD Sydney 2016, or we never met before, here’s the entire recording as I captured it on my machine.

I decided to just upload the raw file with minimum or no video editing, so that I can get it up as soon as possible.

Let me know what you think, and what you’d like me to talk about in future videos.

You can tweet at me — @Meligy, and I’ll reply back as soon as I see it.

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.

Fix TypeScript Autocomplete for RxJS Operators And Helpers In NodeJS

I have been working on a Node application, and wanted to use Reactive Extensions (RxJS). All previous interactions with it were in web apps that run in the browser after some SystemJS / Webpack massaging.

At first it looked OK. I could build an Observer, and subscribe to it. I tried to use some operators, and this is when I got stumbled.

The Problem

I had a sample Observable, and I wanted to call flatMapTo on it, but I couldn’t!

Autocomplete only showed the subscribe method, as below:

2016-04-19_07-05-23

Just importing the specific operator file (which modifies the Observable interface and adds the operator method to it) didn’t seem to get the operator autocomplete (or successful compilation at times) to work.

I didn’t want to have to add each operator manually anyway, and this is Node not browser, I don’t have to be picky about imports (and again, it didn’t work anyway).

Note:

I was specifically trying map(), which I was able to get to work with ES2015 targeting (I’m working with Node 5, which has fair ES2015 support).

But that’s because an Observable is also an Iterable object that you can loop over, like an array.

I was not able to get switchMap() (Rx v5), or even it’s v4 equivalent flatMapLatest() though, or any other operator than map() – before I noticed I was not even calling that as an operator.

The documentation suggested the following line.

<code>typings install rxjs-symbol-typings
</code>

But it only threw an error. Which was very weird, because I could find the typings registry entry with this very name.

There were other suggestions for other platforms, which didn’t work for me anyway!

I also struggled to find examples of using RxJS operators with even something like Angular 2. Hence I’m writing this blog post.

How To Get TypeScript Intellisense To Work With RxJS

The main key to working with this was using the “KitchenSink” Definition file. This was also confirmed by looking at:

Property ‘distinct’ does not exist on type Observable<CustomType>

I can use this no problem because I’m in Node and not much worried about the size of the code, etc.

However, this post was not enough to get it to work. I still had a lot to fiddle with. As I mention later, I tried many things, and everything I found working stopped working afterwards.

Until I could nail it down to the following steps:

Install From NPM

You can get that by

<code>npm install @reactivex/rxjs@latest --save
</code>

Make sure you got at least version beta.6, not beta.2.

Add Definition File

You can do this from your tsconfig.json, like:

<code>{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": false,
        "sourceMap": true
    },
    "compileOnSave": true,
    "files": [
        // Typings autocomplete file
        "typings/main.d.ts",

        // RxJS autocomplete
        "node_modules/rxjs/Rx.KitchenSink.d.ts",

        // Your other files come here
        "sample-files.ts"
    ],
    "exclude": [
        "node_modules",
        "typings"
    ]
}
</code>

Or in each TypeScript file if you choose to, at the top of the file:

<code>///<reference path="./node_modules/rxjs/Rx.KitchenSink.d.ts"/>
</code>

Gotchas

For the most part, the previous steps are going to be enough. However, I have found a few more gotchas in different editors, most of them are around WebStorm, which is crazy, as outside Rx, it seems to be an awesome TypeScript IDE.

I like WebStorm because it has the best autocomplete in strings (like paths and imports), and in tsconfig.json (which is outdated in VS Code).

Also, the gotchas sometimes happen in VS Code anyway. I have been going crazy between the two, as soon as I get something to work, undo it and repeat the exact steps, it doesn’t work.

Operator And Type inference

Sometimes type inference doesn’t work. I have found VS Code to be better at this than WebStorm.

<code>var sample1$ = Observable.create(observer => {
    observer.next("Non-proper autocomplete");
    observer.complete();
});
</code>

In this example, autocomplete for sample1$. will give autocomplete for any, which is OK as it’ll accidentally give autocomplete for RXJS operators.

But if you want proper autocomplete:

<code>var sample2$ : Observable<string> = Observable.create(observer => {
    observer.next("It works");
    observer.complete();
});
</code>

Note the : Observable<string> bit.

Observable Helper Methods

These are methods like like of, range, etc. I have found their autcomplete to the worst, it’s very flaky at best.

A few things seemed to help, like importing from "rxjs/onservable".

<code>import {Observable} from "rxjs/Observable";
</code>

(Vs from "import {Observable} rxjs")

I have also found that doing import {Observable} works better than import * as Rx.

When everything fails, going to TypeScript compiler in WebStorm and building current file, or even restarting WebStorm (Yes!!) helps fix autcomplete issues, especially if you changed your references and imports a few times. It just gets confused and doesn’t work well until an IDE restart.

Disclaimer

Just in case it’s not obvious for those not familiar with TypeScript. I have been doing JavaScript for so long. I’m not the person who can’t code without a fully-functional autocomplete.

But since autocomplete is an essential advantage of going the TypeScript way, and I have been loving working with RxJS from the demos I saw, I wanted to take it as a challenge more than anything else to see what it would take to get this working.

Using TypeScript Async/Await With Selenium NodeJS Bindings

In previous post I described how I used TypeScript with Babel to get my JavaScript Selenium tests to be executed by Node.

I wanted to dedicate this separate post to an example of the Selenium code that I wrote, because there are many tricks and gotchas that need your full attention.

That’s mainly around the use of async/await of course. Instead of write a paragraph about each piece of the code, I have embedded my points in the code itself.

The sample simply goes to the Angular2 Github repository, and reports the title and date of the first page of open issues in the repository.

<code>// Following `reference` comments allow Node & WebDriver autocomplete.
// This is needed as both are not written in TypeScript.
// You can download them via TypeScript Definitions package manager:
//      npm install tsd -g
//      tsd install node
//      tsd install selenium-webdriver

/// <reference path="typings/node/node.d.ts" />
/// <reference path="typings/selenium-webdriver/selenium-webdriver.d.ts" />

// If you are not using babel-runtime (see prev post), 
//      you can try this instead. You'll still need babel processing.
// This worked in atom-typescript, didn't work in gulp run.
// Not good to have to write it in every file anyway
require("babel/polyfill");

// The selenium-webdriver offers multiple exports
// Each of them works like namespace
// To use, you need:
//      npm install selenium-webdriver
import * as webdriver from "selenium-webdriver";
import * as chrome from "selenium-webdriver/chrome";

// This is optional good way to manage chromedriver binaries
//      npm install selenium-binaries
// You see I can mix `import` and `require` in same file!
var chromeDriverPath =
    require('selenium-binaries').chromedriver;
process.env.CHROME_DRIVER_PATH = chromeDriverPath;

// Shorten the names we'll use a lot
var By = webdriver.By,
    until = webdriver.until;

// Configure WebDriver. Nothing async yet.
// Could also move into next function no problem.
var driver = new webdriver.Builder()
    .withCapabilities(webdriver.Capabilities.chrome())
    .build();

// You can only use `await` inside an `async` function.
// Hence the IIFE (immediately-invoked function expression) wrapping
(async function() {

    // Some code to ensure browser is closed if I get an error
    // That's trial and error with Selenium, and googling...
    await webdriver.promise.controlFlow().on('uncaughtException',
        async function(e) {
            await driver.quit();
        });
    await process.on('SIGINT', async function() {
        try {
            process.exit();
        } finally {
            await driver.quit();
        }
    });

    // I'll store the results here then console log them.
    // I could log them directly, at 1st I didn't know how
    //      and now I'm keeping it to show more cool tips 
    var results = [];

    try {

        // Most WebDriver functions return special promises 
        //      that allow you to call the next function fluently:
        //      `driver.get(...).findElements(...)`
        // But that's not cool for debugging which part failed
        //      & doesn't work with loops, etc.
        // So, I `await` most of the `promise`s.
        await driver.get("https://github.com/angular/angular/issues");

        var issues = await driver
            .findElements(By.css(".table-list-cell.issue-title"));

        // This is worth its own post:
        // When I use `async` function in `map` (to use `await` in it),
        //      I get an array of promises, not results
        // So, I use Promise.all to
        //      get the results in one promise I can `await`
        results = await Promise.all(issues.map(async function(issue) {

            // Could have used `await` here or did it fluently
            // Just showing different styles.
            var titleContainer = issue
                .findElement(By.css(".issue-title-link"));
            var title = await titleContainer.getText();

            var dateContainer = issue.
                findElement(By.tagName("time"));
            var date = await dateContainer.getAttribute("title");

            return { title: title, date: date };
        }));

        // console doesn't get flushed inside `async` functions
        // Surprisingly, I can `await` it to work!
        await console.log(results);

    } finally {
        await driver.quit();
    }

} ()); // As mentioned above, the function executes itself
</code>

Prerequisites

In case you wonder, here are the packages used above, in a single NPM install command

<code>npm install selenium-webdriver babel selenium-binaries
</code>

This doesn’t involve the build configuration to get this compiled and running.

Check out my other post to learn how to setup the build for this.

Finally, I hope those two articles combined help you write your own asynchronous code with ease, with Selenium or any other purpose.

How To Use Babel And TypeScript, Together!

Use Case

The Selenium bindings for NodeJS are very nice that everything is a promise. When you are sending a command to the browser, this command might pass (say, return the element if found), or fail (say, trying to click an element that doesn’t exist), so, it’s a good fit for promises.

However, when I write tests, I think of them as a sequence of steps. With promises, the next step is always in a then() function.

But no one wants to keep nesting code like
x.then((y)=>y.then( (z)=>z.then(...) )).
Selenium extends promises so that you don’t have to do this all the time, but I soon hit limits (like loops).

Then I remembered reading about TypeScript support for async/await. When I tried it, the coding experience was nice, but I had to do a bit of setup.

Configuring Async / Await In TypeScript

I had to turn some experimental flags on, and target ES6. The easiest way to do this is to create a tsconfig.json file in the directory where I run tsc (TypeScript compiler) from. Here’s what mine mainly looked like:

<code>{
    "compilerOptions": {
        "target": "ES6",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "experimentalAsyncFunctions": true
    },
    "files": [
        "app/**/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}
</code>

Node, ES6, Babel, And ES7

I tried to run the output of TypeScript in Node, but it didn’t work. It complained about import statements. It seems that Node doesn’t understand ES6 module system, not very surprising given it has its own module system too, commonJS.

TypeScript can target commonJS, but async/await is only supported when targeting ES6.

So, I decided to take the output through Babel before I run it with NodeJS.

Side Note: Babel already had support for async/await as it’s also coming to ES7. But by the time I got to that I was already happy with my autocomplete experience in TypeScript that I wanted to continue with this setup.

Using Atom Editor

It was so easy to get babel to work with TypeScript in Atom. I had atom-typescript package installed, and it suppoted an extra property in tsconfig.json:

<code>"externalTranspiler": "babel"
</code>

This is a NON-standard TypeScript property. But it allowed me to work very happily with Atom, and my .ts files processed via TypeScript and Babel whenever I save them.

This approach had a main no-go issue though. Anyone who pulls my code should be able to get it to work without me committing the “auto-generated” JS files, and without the others having my Atom setup, and building the project from Atom. This won’t work in CI build server.

Using Gulp Tasks

At this point, I decided to have a gulp task that does the transformation. It turned out to be very easy.

The following code sets gulp to process all .ts files in “app” folder via TypeScript and Babel, and save them as .js files:

<code>var gulp = require("gulp");
var ts = require("gulp-typescript");
var babel = require("gulp-babel");
var rename = require("gulp-rename");

gulp.task("ts-babel", function () {
    // Using my existing tsconfig.json file
    var tsProject = ts.createProject(__dirname + "/tsconfig.json");

    // The `base` part is needed so
    //  that `dest()` doesnt map folders correctly after rename
    return gulp.src("app/**/*.ts", { base: "./" })
        .pipe(ts(tsProject))
        .pipe(babel({
            optional: ["runtime"]
        }))
        .pipe(rename(function (path) {
            path.extname = ".js";
        }))
        .pipe(gulp.dest("."));
});
</code>

You might wonder why did I do both transformations in the same task. That’s mainly to save disk writes, so that I don’t have to write an ES6 file that’s never going to be used except for Babel processing.

If you want to split them, should be easy, like:

<code>var gulp = require('gulp');
var ts = require('gulp-typescript');
var babel = require('gulp-babel');
var rename = require('gulp-rename');

gulp.task('tsc', function() {
    var tsProject = ts.createProject(__dirname + '/tsconfig.json');
    return gulp.src('app/**/*.ts', { base: './' })
        .pipe(ts(tsProject))
        .pipe(rename(function (path) {
            path.extname = '.babel';
        }))
        .pipe(gulp.dest('.'));
});

gulp.task('babel', function() {
    return gulp.src('app/**/*.babel', { base: './' })
        .pipe(babel())
        .pipe(rename(function (path) {
            path.extname = '.js';
        }))
        .pipe(gulp.dest('.'));
});

gulp.task('ts-babel', ['tsc', 'babel']);
</code>

NPM Prerequisites

To run these, you need to install the needed packages:

<code>npm install gulp gulp-typescript gulp-babel gulp-rename babel-runtime --save-dev
</code>

Example & Conclusion

So, that’s all it takes to run the compilation/transpilation. You might want to hook this into nodemon or some gulp-watch task etc., but this is not specific to TypeScript or babel in any way, so, I thought it’s not worth mentioning here.

If you are curious about the code that I needed this setup for, check out my sample code post.