Scaffold an Express Front End with Angular CLI

In my last post I explained how to build scalable Web API’s with Express and Async/Await.  The next chapter in the story is to build a cross-platform front end that consumes the back end Web API.  While there are a number of cross-platform UI technologies to choose from, such as Xamarin (C#), Swing (Java) or Kivy (Python), Angular (JavaScript) has garnered attention because it crosses the chasm that has separated web, desktop and mobile platforms, and it’s designed to be used with TypeScript, which increases developer productivity by adding static types and incorporating features from current and future versions of JavaScript.

Clone, fork or download the code for this post: https://github.com/tonysneed/Demo.Express.Angular-CLI

angular2-logo

Starting from Scratch

If you’re getting up to speed with Angular, you’ll want to check out the quickstart guide and source code, which describe how to build an Angular application from scratch.  To set up an Angular Hello World app, you’ll need to perform the following steps:

  1. Initialize package.json
  2. Install Angular packages
  3. Set TypeScript options with tsconfig.json
  4. Configure SystemJS with systemjs.config.js
  5. Add index.html host web page
  6. Create root app module
  7. Create root app component
  8. Create root app template
  9. Add main.ts platform bootstrapper

Whew! That’s a lot to go through to get started with a basic Angular application — and you haven’t even added your first component!

Introducing Angular CLI

To keep you from having to perform all these steps manually, Angular has created a tool called Angular CLI (in beta at the time of this writing), which you can use to scaffold new Angular projects, as well as add components to existing projects.

angular-cli

After installing Node.js, install angular-cli globally using NPM (Node Package Manager). While the documentation points you in the direction of creating a new app with Angular CLI, what I’d like to illustrate in this blog post is using Angular CLI to add a web front end to an existing project that includes a Web API built using Node with Express.

Begin with Express

For this purpose, I have created a starter branch in the code repository for this post. If you clone the repo, you can easily switch to the starter branch using GitHub Desktop.

ng-demo-starter.png

Begin by launching Visual Studio Code by opening a terminal at the repo location and entering  code. (you can use another code editor if you prefer).  Press Cmd + ` to open the integrated terminal in VS Code and enter npm install to restore package dependencies.  In the README.md file you’ll find instructions for building and running the Web API, namely, press Cmd + P and enter task serve-api.

Open a browser to localhost:/3000/api/products, and you should see JSON for several products.  Then terminate the running task by pressing Cmd + Shift + P, entering Terminate and selecting the first item.  (You may want to create a keyboard shortcut to terminate running tasks.)

products-json.png

Get Productive

Now that you have a functioning Web API using Express, it’s time to add a front end using Angular-CLI.  Open a terminal at the project root and enter ng init.  You’ll be prompted to overwrite a few files.  Enter Y to overwrite each of the files.  Don’t worry, you’ll use VS Code’s Git integration to see file differences and restore the original contents.

ng-init-overwrite-png.png

Back in VS Code, click on the Git icon to view file changes.  To restore the original content to overwritten files, simply select the changed file and copy content from the left to the right side.  You don’t need to change tslint.json, and the only change to .gitignore will be to add the dist folder.  But you’ll need to copy dependencies from the original to the new package.json file.

ng-init-package-diff.png

After updating both dev and runtime dependencies, run npm install to add packages required by Angular CLI.  Then go back to the terminal and enter npm start, which in turn will run ng serve to start a web server with browser live reload.  Open a browser to localhost:4200/, and you’ll see the root app component hosted in index.html.

ng-new-browser.png

To prove that live reload is working, you can open app.component.ts in VS Code and change the title property to 'Hello Angular CLI!'.  A few seconds later, you’ll see the change reflected in the browser without you having to refresh the page.  To stop the web server, go back to the terminal and press Ctrl + C.

Add Some Style

While Angular CLI created a functioning Angular app in very little time, the look is rather plain.  To spice things up, you’ll want to add some styling with a framework such as Angular Material or Bootstrap.  Because we’re building a straightforward app for this demo, Bootstrap will do fine.

For instructions on adding Bootstap with an app generated by Angular CLI, see the section called Global Library Installation in the ReadMe for the Angular CLI project on GitHub, as well as the ReadMe for ng2-boostrap.  Start by running:

npm install ng2-bootstrap bootstrap jquery --save

Open angular-cli.json and insert a new entry into the styles array.

"styles": [
        "styles.css",
        "../node_modules/bootstrap/dist/css/bootstrap.min.css"
],

Next insert two entries into the scripts array.

"scripts": [
  "../node_modules/jquery/dist/jquery.js",
  "../node_modules/bootstrap/dist/js/bootstrap.js"
],

Now you can apply Boostrap styles to HTML in your application.  Start by placing a <div> around the <h1> tag in app.component.html and setting class to "container".

<div class="container">
  <h1>
    {{title}}
  </h1>
</div>

This will apply the container bootstrap style to the root app component.  You may also wish to apply a different style to elements in the app component. You can accomplish this by setting styles in app.component.css.

h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}

With these changes in place, start the app by running npm start and browse to the home page. You should see a more attractive user interface.

add-bootstrap.png

Your First Component

Now it’s time to scaffold your first component — how exciting!  Once again, Angular CLI will lend a helping hand.  To find out what it can do for you, have a look at the Angular CLI Reference document, which lists top-level commands.  Or you can enter ng --help at the command line, and then drill into a command by appending the name of the command you want to explore.  For example, to find out about the generate command enter: ng --help generate.  From the output you’ll see items you can generate (called “blueprints”).

Let’s start by generating a product class.  Since you may wish to share it among various components, you create  shared/models directories beneath app, then run:

ng generate class shared/models/product

This will generate a Product class in the shared/models folder, but it doesn’t have any properties. You can simply add a constructor with public parameters for each property you’d like to add.

export class Product {
  constructor(
    public productId: number,
    public productName: string,
    public unitPrice: number) { }
}

Now that you have a model, the next step is to generate a component, which you can produce by running ng generate with the component blueprint.  If you’re tired of typing generate every time, you can substitute it with the alias g.

ng g component products

This will create a ProductsComponent class along with an HTML template, a CSS file and a spec file for testing the component, and place them all in a products folder under app.

Angular is a framework for building SPA’s (Single Page Applications) and implements an MVVM (Model-View-ViewModel) architecture, with components playing the role of the ViewModel and templates serving as the View.  The way you associate components and templates is by applying an @Component decorator to a component and include a templateUrl.

Open products.component.ts and import Product, then add a title property set to ‘Products’ and a products property that is an array of Product.  Later you’re going to set products by retrieving products from the Express Web API in your project, but for now you can simply initialize products in the ngOnInit function to some test products.  You should also include an error property in the unlikely event something goes wrong.🙂

import { Component, OnInit } from '@angular/core';
import { Product } from '../shared/models/product';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {

  title: string = 'Products';
  products: Product[];
  error: any;

  constructor() { }

  ngOnInit() {
    this.products = [
            new Product(1, 'Product 1', 10),
            new Product(2, 'Product 2', 20),
        ];
  }
}

Now that you have a component for products, you can create a template by opening products.component.html and replacing generated content with HTML that contains bindings to properties in the component. Angular uses double curly braces for what is called interpolation to evaluate an expression that can refer to component properties.  You can also use built-in directives, such as *ngIf and *ngFor to perform data binding in the template.  For the products template, we’ll insert an HTML table with bootstrap styling that lists the properties of each product. (Note in the following HTML snippet, you’ll need to substitute left and right brackets and ampersands within the pre tags.)

<div class='panel panel-primary'>
    <div class='panel-heading'>
        {{title}}
    </div>
    <div class='panel-body'>
        <div class='table-responsive'>
            <pre>&lt;table class='table' *ngIf="products &amp;&amp; products.length"&gt;</pre>
                <thead>
                    <tr>
                        <th>Product Name</th>
                        <th>Unit Price</th>
                    </tr>
                </thead>
                <tbody>
                    <pre>&lt;tr *ngFor="let product of products"&gt;</pre>
                        <td>{{ product.productId }}</td>
                        <td>{{ product.productName }}</td>
                        <td>{{ product.unitPrice }}</td>
                    </tr>
                </tbody>
            </table>
        </div>
        <pre>&lt;div class="alert alert-danger" *ngIf="error"&gt;</pre>
            <strong>Error!</strong> {{error}}
        </div>
    </div>
</div>

Lastly, you’ll need to edit app.component.html to include the 'app-products' selector from products.component.ts.

<div class="container">
  <h1>
    {{title}}
  </h1>
  <pre>&lt;app-products&gt;&lt;/app-products&gt;</pre>
</div>

Run npm start from a command prompt and navigate to http://localhost:4200/. You should see the products component displayed in the browser.

products-browser.png

Connect to the Web API

While we have a products component that works, it does not yet display products retrieved from the back end Express Web API.  To accomplish this, we’ll use Angular CLI to generate a products service.

ng g service shared/services/products

Add a private parameter to the constructor to inject the Angular Http client into the products service, then add a getProducts function that calls this._http.get and converts it to a promise, so that you can handle success by calling .then, or failure by calling .catch. Here is the code for the ProductsService class.

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';

import { Product } from '../models/product';
import { Urls } from '../constants';

@Injectable()
export class ProductsService {

  private _productsUrl = Urls.BaseUrl + 'api/products';

  constructor(private _http: Http) { }

  getProducts(): Promise {
    return this._http.get(this._productsUrl)
      .toPromise()
      .then(resp => resp.json() as Product[])
      .catch(this.handleError);
  }

  private handleError(error: any): Promise {
    console.error('An error occurred', error);
    return Promise.reject(error.message || error);
  }
}

In order to use ProductsService, you’ll need to add it as a provider to @NgModule in app.module.ts, after importing the ProductsService module.  Lastly, you’ll need to refactor ProductsComponent to use ProductsService, by adding a private parameter to the constructor, then calling getProducts to set the products property if successful or the error property if unsuccessful.

import { Component, OnInit } from '@angular/core';
import { Product } from '../shared/models/product';
import { ProductsService } from '../shared/services/products.service';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {

  title: string = 'Products';
  products: Product[];
  error: any;

  constructor(private _productService: ProductsService) { }

  ngOnInit() {
    // Replaced with products service
    // this.products = [
    //         new Product(1, 'Product 1', 10),
    //         new Product(2, 'Product 2', 20),
    //     ];

    this._productService.getProducts()
      .then(products => this.products = products)
      .catch(error => this.error = error);
  }
}

See It in Action

It’s finally time to run the front and back ends of your Angular-Express application at the same time. But for it to succeed, you’ll need to add a proxy to the back end so that calls to the Web API on port 4200 will be sent to port 3000 instead.  Add a file to the project root called proxy.conf.json with the following content:

{
  "/api": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

Then update the start script in package.json to append a proxy-config parameter.

"start": "ng serve --proxy-config proxy.conf.json"

With the back end proxy configured, you can start the Express Web API by running the serve-api gulp task.  Then you can start the Angular app by running npm start.  You should now see a list of products that have been retrieved from the Web API.

products-final-browser.png

Clone, fork or download the code for this post: https://github.com/tonysneed/Demo.Express.Angular-CLI

Congratulations!  You have used Angular CLI to scaffold a front end to an Express Web API back end.  Because the front end is independent of the back end, there’s nothing to stop you from swapping out the Express implementation for another technology stack, such as ASP.NET Core, without affecting the Angular front end.

Posted in Technical | Tagged , , | 3 Comments

Scalable Web API’s with Express and Async-Await

If you’re a C# developer, you’ve most likely fallen in love with async / await. It makes writing asynchronous code as natural as writing non-asynchronous code, resulting in code that is easier to read, debug and maintain.

The exciting news is that async / await goodness has found its way into the world of JavaScript!

async-await-js.png

Currently async / await is a proposed feature of ECMAScript 2017 (ES8) and is moving from stage 3 (candidate) to stage 4 (finished) in the TC39 process.  But there’s no need to wait for the release of ES2017 — with the help of JavaScript transpilers, such as Babel and Typescript, which can translate future versions of JavaScript into downlevel versions compatible with most browsers, you can start using async / await today!

Clone, fork or download the code for this post: https://github.com/tonysneed/Demo.Express.Async

Because of features like type-safety, interfaces and generics, I’m an unabashed TypeScript fanboy, so I’m going to talk about how to use async / await with TypeScript.  But the approach is the same using the async functions Babel plugin.

TypeScript has had support for async / await on the server-side for Node with ES2015 since version 1.7, but it will add support for async / await on the client-side for ES5/ES3 with version 2.1.  In this blog post, I’ll focus on using async / await to create RESTful Web API’s with Express, a minimalist web framework for Node.

The primary motivation for async / await on the server-side is scalability with Web API’s that perform asynchronous I/O operations.  So if you are performing database queries, calling other web services or interacting with the file system, then it’s imperative that you not block the executing thread waiting for I/O operations to complete.  As I demonstrated in a prior post on async functions in C#, blocking threads on a server will result in creating additional threads, each of which carries a great deal of overhead, both in terms of memory and CPU utilization, which can impact application performance.

From CallbackHell to Async-Await Heaven

In the JavaScript world, you’re less likely to use a synchronous API in an Express application, because just about everything is done with callbacks.  The problem is that you can easily find yourself slipping into what is sometimes referred to as callback hell.  Here is a brief example:

try {
    greeter.getGreeting(g => {
        console.log(g);
        try {
            greeter.getGreeting(g => {
                console.log(g);
                try {
                    greeter.getGreeting(g => {
                        console.log(g);
                    });
                } catch (error) {
                    console.log(error.message);
                }
            });
        } catch (error) {
            console.log(error.message);
        }
    });
} catch (error) {
    console.log(error.message);
}

Promises were invented to help prevent callback hell. Using the getGreeting function as an example, you can refactor it to return a promise, in which you call resolve to return a result or reject to throw an error.

getGreeting(): Promise<any> {
    return new Promise((resolve, reject) => {
        try {
            let greeting = generateGreeting();
        } catch (error) {
            reject(error);
        }
        resolve("Hello");
    });
}

The caller of getGreeting can then handle success and failure by calling then or catch on the returned promise, and catch will handle an error from any of the preceeding then handlers.

greeter.getGreeting()
    .then(g => {
        console.log(g);
        return greeter.getGreetingAsync();
    })
    .then(g => {
        console.log(g);
        return greeter.getGreetingAsync();
    })
    .then(g => {
        console.log(g);
    })
    .catch(e => {
        console.log(e);
    });

While an improvement over callback hell, promises still require async code that is quite different than synchronous code.  To help close the gap, async / await makes it possible to write asynchronous code in much the same way as synchronous code.

async function main(): Promise<void> {
    try {
        let g1 = await greeter.getGreeting();
        console.log(g1);
        let g2 = await greeter.getGreeting();
        console.log(g2);
        let g3 = await greeter.getGreeting();
        console.log(g3);
    }
    catch (error) {
        console.log(error);
    }
}

Each call to getGreeting will take place sequentially without any blocking, and a single catch block will handle errors from any awaited method.

Async-Await with Express

When building a Web API that performs I/O against a data store, it’s helpful to use a repository pattern, so that you can mock the repository without external dependencies, as well as swap out one data access API for another should the need arise.  In the code for this post, I created a ProductsRepository class that uses an in-memory collection to simulate a persistent data store.  It imports a Product class with properties for productId, productName and unitPrice.

class Product {
    constructor(
        public productId: number,
        public productName: string,
        public unitPrice: number) { }
}

export { Product };

ProductsRepository has promise-based methods for retrieveAll, retrieve, create, update and delete, which call resolve if successful or reject if the id is not valid.

import { Product } from "../models/product";

// Methods return promises to simulate IO-bound operations

export default class ProductsRepository {

    // Array of products
    private _products = [
        new Product(1, "Chai", 10),
        new Product(2, "Espresso", 20),
        new Product(3, "Capuccino", 30),
        new Product(4, "Macchiato", 40),
        new Product(5, "Americano", 50),
        new Product(6, "Flat White", 60),
    ];

    retrieveAll(): Promise<Product[]> {
        return new Promise((resolve, reject) => {
            resolve(this._products);
        });
    }

    retrieve(id: number): Promise<Product> {
        return new Promise((resolve, reject) => {
            let product = this.getProduct(id);
            if (product === null) {
                reject(`Invalid id: ${id}`);
            }
            resolve(product);
        });
    }

    create(product: Product): Promise<Product> {
        return new Promise((resolve, reject) => {
            if (this.getProduct(product.productId) !== null) {
                reject(`Product exists with id: ${product.productId}`);
            }
            this._products.push(product);
            resolve(product);
        });
    }

    update(product: Product): Promise<Product> {
        return new Promise((resolve, reject) => {
            let existingProduct = this.getProduct(product.productId);
            if (existingProduct === null) {
                reject(`Invalid id: ${product.productId}`);
            }
            let index = this._products.indexOf(existingProduct);
            this._products[index] = product;
            resolve(product);
        });
    }

    delete(id: number): Promise<void> {
        return new Promise((resolve, reject) => {
            if (this.getProduct(id) === null) {
                reject(`Invalid id: ${id}`);
            }
            this._products.splice(id - 1, 1);
            resolve();
        });
    }

    private getProduct(id: number): Product | null {
        let products: Product[] = this._products
            .filter(p => p.productId === id);
        if (products.length > 0) {
            return products[0];
        }
        return null;
    }
}

In Express it’s customary to create a router to handle requests for a path segment, such as api/products.  The idea is similar to controllers in ASP.NET Web API and promotes improved modularity.  Here is a router that calls promise-returning functions on the products repository to perform I/O operations asynchronously.  But rather than resort to Promise’s then and catch methods, we use async / await so that the code looks cleaner and is easier to undersand, including the use of try / catch for error handling.

import * as express from "express";
import { Request, Response } from "express";

import { Product } from "../models/product";
import ProductsRepository from "../services/products-repo";

let router = express.Router();
let productsRepo = new ProductsRepository();

// GET route
router.get("/", async (req: Request, resp: Response) => {
    console.log("Retrieving products");
    try {
        let products = await productsRepo.retrieveAll();
        resp.json(products);
    } catch (error) {
        console.log(error);
        resp.sendStatus(500);
    }
});

// GET route with id
router.get("/:id", async (req: Request, resp: Response) => {
    console.log(`Retrieving product id ${req.params.id}`);
    try {
        let product = await productsRepo.retrieve(+req.params.id);
        resp.json(product);
    } catch (error) {
        console.log(error);
        if (error.indexOf("Invalid id") > -1) {
            resp.sendStatus(404);
            return;
        }
        resp.sendStatus(500);
    }
});

// POST route
router.post("/", async (req: Request, resp: Response) => {
    console.log(`Creating product: ${JSON.stringify(req.body)}`);
    try {
        let product = await productsRepo.create(req.body);
        resp.json(product);
    } catch (error) {
        console.log(error);
        if (error.indexOf("Product exists") > -1) {
            resp.sendStatus(400);
            return;
        }
        resp.sendStatus(500);
    }
});

// PUT route
router.put("/", async (req: Request, resp: Response) => {
    console.log(`Updating product id ${req.body.productId} to: ${JSON.stringify(req.body)}`);
    try {
        let product = await productsRepo.update(req.body);
        resp.json(product);
    } catch (error) {
        console.log(error);
        if (error.indexOf("Invalid id") > -1) {
            resp.sendStatus(404);
            return;
        }
        resp.sendStatus(500);
    }
});

// DELETE route with id
router.delete("/:id", async (req: Request, resp: Response) => {
    console.log(`Deleting product id ${req.params.id}`);
    try {
        await productsRepo.delete(+req.params.id);
        resp.end();
    } catch (error) {
        console.log(error);
        if (error.indexOf("Invalid id") > -1) {
            resp.sendStatus(404);
            return;
        }
        resp.sendStatus(500);
    }
});

// Export products router module
export { router as productsRouter };

To use async / await with Node, you’ll need a tsconfig.json file at the root of your Web API that specifies a target of es2015. Then add a server.ts file to bootstrap Express and plug the products router into the pipeline.  You can smoke test your Web API using a tool such as Fiddler or Postman.

Enjoy!

Posted in Technical | Tagged , | Leave a comment

Now is a Great Time to Learn TypeScript

Today I am pleased to share some exciting news.  I just finished authoring a new 5-day course on TypeScript!

Essential TypeScript 2.0 with Visual Studio Code

It is timed to coincide with the 2.0 release of TypeScript, a relatively new programming language designed to make JavaScript strongly typed and more capable of supporting large-scale web applications.

gk-dm.png

I authored the course for Global Knowledge, an IT training company that in 2015 acquired DevelopMentor, a developer training company where I authored and taught courses in C# and .NET since 2006.  The course represents the culmination of a four-month odyssey, in which I not only had to grok TypeScript grammar and syntax but also mastered an entirely new technology stack and toolchain.

It was fun!

Here is a list of topics included in the course:

  1. Introduction to TypeScript
  2. Language Basics
  3. Using Visual Studio Code with TypeScript
  4. Task Automation, Unit Testing, Continuous Integration
  5. The TypeScript Type System
  6. Functional Programming
  7. Asynchronous Programming
  8. Object-Oriented Programming
  9. Generics and Decorators
  10. Namespaces and Modules
  11. Practical TypeScript with Express and Angular

While performing the herculean task of creating a 5-day class with slides, labs and numerous demos, I have to say I thoroughly enjoyed the process of adding a new weapon to my arsenal as a software developer and the chance to venture off in an entirely new direction.  So I thought I would take this opportunity to put together some thoughts about why I think this is a great time to learn web development with TypeScript — and at the same time plug my new course.🙂

Revenge of JavaScript

One motivation for me to pick up JavaScript (of which TypeScript is a superset) is that we no longer live in a world in which Windows is dominant, and JavaScript can be used to write apps for more than just web browsers — you can use it to write desktop and mobile applications, as well as backend services running in the Cloud.  It has unwittingly become one language to rule them all.

express-logo.jpg

Secondly, web development has matured to the point where it is possible to write an app that has nearly the same interactivity and responsiveness as a traditional desktop application.  One of the reasons I previously gravitated toward WPF (and unconsciously drifted away from ASP.NET) was that desktop and native mobile apps seemed to offer a more fluid user experience where you could work with data locally without having to continually refresh the screen from a remote server.

All that has changed with the advent of SPA’s (Single Page Applications), where turbocharged JavaScript engines quickly render rich, interactive web pages.  It’s the perfect time to build SPA’s because second generation frameworks have emerged that take web development to a whole new level and implement the MVVM (Model-View-ViewModel) pattern (or some MV-* variation), providing benefits such as better separation of concerns, testability and maintainability.  Frameworks like Angular, Aurelia and ReactRedux also provide tools for quickly scaffolding new applications and preparing them for production.

angular2-logo

TypeScript has emerged as the language of choice for building many of these kinds of modern web apps, because its strong-typing enables features we’ve come to take for granted, such as interfaces and generics. It also provides things most developers couldn’t think of doing without, such as intellisense, statement completion code refactorings.  And TypeScript’s popularity received a shot in the arm from the Angular team at Google, which is using it to build Angular.  In addition, most of their tutorials are written in TypeScript.

JavaScript Has Grown Up

In 2015 JavaScript had its most significant upgrade since it was created in 1995 by Brendan Eich in a 10-day hackathon.  With the release of ECMAScript 2015, JavaScript received a slew of new features, many of which classical languages, such as Java and C#, have had for years.  These include classes, inheritance, constants, iterators, modules and promises, to name a few.  TypeScript not only includes all ES 2015 features, but it fast-forwards to future versions ECMAScript by supporting proposed features such as async and await operators, which help simplify asynchronous code.  TypeScript lets you use advanced features of JavaScript by transpiling down to ES5, a flavor of JavaScript compatible with most browsers.

es2015

When you put modern JavaScript together with TypeScript, you get a powerful combination that gives you just about everything you might want for building SOLID applications that can run in the browser, on the server or on mobile and desktop platforms.

Shiny New Tools

The nice thing about TypeScript is that you’re free to use whatever tool you like, from a full-fledged IDE like Visual Studio or Web Storm, to a lightweight code editor, such as SublimeText, Atom, Brackets or Visual Studio Code.  While there’s nothing wrong with any of these options, I prefer using VS Code for TypeScript development, because it comes with TypeScript in the box and the team eats their own dogfood by using TypeScript to build the editor.

vs-code

Coming from a C# background, where I was confined to using Visual Studio on Windows with its enormous overhead, I can run code editors such as VS Code on my Mac, giving me one less reason to fire up a Windows VM.  VS Code starts quickly, and I can open it at a specific folder from either the Finder or Terminal.  I also found navigation in VS Code to be straightforward and intuitive, and you can perform many tasks from the command palette, including custom gulp tasks.  VS Code functions as a great markdown editor with a side-by-side preview that refreshes in real time as you make changes.  It has Git integration and debugging support, as well as a marketplace of third-party extensions that provide a variety of nifty services, such as TypeScript linting and Angular 2 code snippets.  Put it all together, and VS Code is a perfect fit for TypeScript development.

Living in Harmony

One of the most compelling reasons I can think of for picking up TypeScript is that it’s the brainchild of the same person who created C#, Anders Hejlsberg, who in a prior life also invented Turbo Paschal and Delphi.  Having such an amazing track record, I have a high degree of confidence in following him into the world of web and native JavaScript development, where he has made it possible to be more productive and write code that is more resilient because the TypeScript compiler is able to catch problems at development-time that would otherwise only become apparent at runtime.

Lastly, it’s significant that Anders did not choose to create a language that is not JavaScript, such as CoffeeScript, but rather one that includes all of JavaScript with optional type annotations that disappear when TypeScript is compiled down to plain old JavaScript.  In fact, all JavaScript is valid TypeScript, and you can put in annotations or leave them out wherever you like, giving you the best of both dynamic and static typing.  In other words, TypeScript does not impose itself on you or dictate that you follow any of its prescriptions.

To conclude, this is a great time for learning TypeScript, even if you don’t know much about JavaScript, because the two are mostly one and the same, except for extra features designed to make your life easier and that allow you to write JavaScript programs capable of growing over time without becoming too unwieldy.  This frees you to concentrate on learning JavaScript frameworks, such as Express and Angular, that empower you to build RESTful web services and modern client applications.

Happy coding!

Posted in Technical | Tagged | Leave a comment

Simple MVVM Toolkit – It Lives!

Update: Version 1.0 of Simple MVVM Toolkit Express has now been released and is based on version 1.0 RTM of .NET Core: https://www.nuget.org/packages/SimpleMvvmToolkit.Express. Source code and samples can be found here: https://github.com/SimpleMvvm

Now that .NET Core is stable and RC2 has been released, and the .NET Platform Standard has been proposed to replace Portable Class Libraries, I thought it would be a good idea to port my Simple MVVM Toolkit to .NET Core and provide support for additional platforms, such as Universal Windows Platform and the latest version of Xamarin for cross-platform mobile apps, included iOS from Apple and Android from Google.

dotnet-core.png

Rather than update my existing repository, I decided it was time for a fresh start.  So I createed a new project on GitHub called Simple MVVM Toolkit Express.  It is compatible with the following platforms:

  • Portable Class Libraries: Profile 111 – .NET 4.5, AspNet Core 1.0, Windows 8, Windows Phone 8.1
  • .NET Framework 4.6
  • Universal Windows Platform 10.0
  • Mono/Xamarin: MonoAndroid60, XamariniOS10
  • .NET Core 1.0: NetStandard 1.3

I decided to break compatibility with the following legacy frameworks: .NET 4.0 and Silverlight.

The toolkit has all the major features of the classic version, including classes for models and view models, support for validation and editing with rollbacks, as well as a leak-proof message bus (aka mediator or event aggregator).  Platform-specific threading implementations have been removed, because it’s better to use C#’s built-in async support.

I published a pre-release NuGet package, which you can find here: https://www.nuget.org/packages/SimpleMvvmToolkit.Express.  And I’ve created samples for WPF, UWP and Xamarin, which you can find on the SimpleMvvm home repository.

I used the dotnet CLI (command-line interface) tool chain to build the project and generate a multi-targeted NuGet package, but I had to modify the generated nuspec file to work around some compatibility issues.  In the end, it was a great learning experience, and I found it reassuring that I could continue to use a popular framework for building many different kinds of client applications using the Model-View-ViewModel design pattern.

Happy coding!

Posted in Technical | 12 Comments

Getting Visual Studio Code Ready for TypeScript: Part 3

Part 3: Injecting Scripts with Gulp

This is the third part in a series of blog posts on Getting Visual Studio Code Ready for TypeScript:

  1. Compiling TypeScript to JavaScript
  2. Writing Jasmine Tests in TypeScript
  3. Injecting Scripts with Gulp (this post)

Leveraging Gulp

In the first and second post in this series I showed how you can use Gulp to automate common tasks such as compiling TypeScript to JavaScript and running Jasmine tests in a browser.  While Gulp is not strictly necessary to perform these tasks, it allows you to chain together multiple tasks, which can give you a smoother workflow.

gulp-partial

You can download a sample project with code for this blog post.  You can also download my Yeoman generator for scaffolding new TypeScript projects for Visual Studio Code.

For example, we defined a “watch” task with a dependency on the “compile” task, so that Gulp performs a compilation before watching for changes in any TypeScript files.  When changes are detected, the “compile” task is then re-executed.

gulp.task('compile', function () {

    exec('rm -rf dist && tsc -p src');
});

gulp.task('watch', ['compile'], function () {

    return gulp.watch('./src/**/*.ts', ['compile']);
});

Likewise, we defined a “test” task with a dependency on the “watch” task, so that changes to any TypeScript files will cause browser-sync to reload the browser when it detects that the JavaScript files have been re-generated.

gulp.task('test', ['watch'], function () {

    var options = {
        port: 3000,
        server: './',
        files: ['./dist/**/*.js',
                './dist/**/*.spec.js',
                '!./dist/**/*.js.map'],
        // Remaining options elided for clarity
    };

    browserSync(options);
});

Listing Tasks

While VS Code allows you to execute gulp tasks from within the editor, you may sometimes prefer to use Gulp from the Terminal (if for no other reason than to see all the pretty colors).  To make this easier, we can use a plugin that will list all the tasks we’ve defined in our gulpfile.js.  But before we get into that, we can make our lives easier by using a plugin called gulp-load-plugins, which will relieve us from having to define a separate variable for each plugin we wish to use.  All we need to do is define a $ variable, then use it to execute other gulp plugins we’ve installed.

var $ = require('gulp-load-plugins')({ lazy: true });

To list tasks in gulpfile.js, we can define a “help” task which uses the gulp-task-listing plugin to list all of our tasks.  We’ll follow a convention which uses a colon in the task name to designate it as a sub-task.  We can also define a “default” task which calls the “help” task when a user enters “gulp” in the Terminal with no parameters.

gulp.task('help', $.taskListing.withFilters(/:/));
gulp.task('default', ['help']);

You’ll need to install both Gulp plugins using npm.

npm install --save-dev gulp-load-plugins gulp-task-listing

Then open the Terminal, type “gulp” (no quotes) and press Enter.  You should see a list of tasks displayed.  To execute a task, simply type “gulp” followed by a space and the name of the task.

gulp-help

Injecting Scripts

In my last blog post I described how you can run Jasmine tests in a browser by serving up an HTML file which included both source and spec JavaScript files.  But this required you to manually insert script tags into SpecRunner.html.  You might have asked yourself if there might be a way to inject scripts into the spec runner automatically whenever you executed the “test” task. Well it just so happens: there’s plugin for that!™ It’s appropriately called gulp-inject, and you can add an injectScripts function to gulpfile.js which will inject scripts into SpecRunner.html based on globs for source and spec files.

var inject = require('gulp-inject');

function injectScripts(src, label) {

    var options = { read: false, addRootSlash: false };
    if (label) {
        options.name = 'inject:' + label;
    }
    return $.inject(gulp.src(src), options);
}

Now add a “specs:inject” gulp task which calls injectScripts to insert the source and spec scripts.  Because we only intend to call this task from other tasks, we can classify it as a sub-task by inserting a colon in the task name.

gulp.task('specs:inject', function () {

    var source = [
        './dist/**/*.js',
        '!./dist/**/*.js.map',
        '!./dist/**/*.spec.js'];

    var specs = ['./dist/**/*.spec.js'];

    return gulp
        .src('./specrunner.html')
        .pipe(injectScripts(source, ''))
        .pipe(injectScripts(specs, 'specs'))
        .pipe(gulp.dest('./'));
});

The gulp-inject plugin will insert selected scripts at each location, based on a comment corresponding to the specified label.  Simply edit SpecRunner.html to replace the hard-coded script tags with specially formatted comments. After running the “specs:inject” task, you should see the appropriate scripts inserted at these locations

<!-- inject:js -->
<!-- endinject -->

<!-- inject:specs:js -->
<!-- endinject -->

Injecting Imports

In addition to inserting source and spec scripts, you’ll also want to inject System.import statements into the spec runner so that system.js can provide browser support for module loading.  For that you’ll need to install packages for glob, path, gulp-rename, and gulp-inject-string, then add an injectImports function to gulpfile.js.

var glob = require('glob');
var path = require('path');

function injectImports(src, label) {

    var search = '/// inject:' + label;
    var first = '\n    System.import(\'';
    var last = '\'),';
    var specNames = [];

    src.forEach(function(pattern) {
        glob.sync(pattern)
            .forEach(function(file) {
                var fileName = path.basename(file, path.extname(file));
                var specName = path.join(path.dirname(file), fileName);
                specNames.push(first + specName + last);
            });
    });

    return $.injectString.after(search, specNames);
}

Then add an “imports:inject” task which calls injectImports to insert system imports into a file called system.imports.js.

gulp.task('imports:inject', function(){

    gulp.src('./util/system.template.js')
        .pipe(injectImports(['/.dist/**/*.spec.js'], 'import'))
        .pipe($.rename('./util/system.imports.js'))
        .pipe(gulp.dest('./'));
});

Modify SpecRunner.html to replace the script that uses System.import with a reference to system.imports.js.

<script src="util/system.imports.js"></script>

When you execute the “imports:inject” gulp task, it will search a file called system.template.js for a triple-dash comment with the text “inject:import”, where it will inject imports for each spec file. The result will be written to system.imports.js.

Promise.all([
    /// inject:import
    System.import('dist/greeter/greeter.spec'),
    System.import('dist/italiangreeter/italiangreeter.spec')
]);

Lastly, you need to update the “test” task in gulpfile.js to add the two sub-tasks for injecting scripts and imports. This will ensure they are executed each time you run your tests.

gulp.task('test', ['specs:inject', 'imports:inject', 'watch'], function ()

Debugging Gulp Tasks

If you run into problems with any gulp tasks, it would help if you could set breakpoints in gulpfile.js, launch a debugger and step through your code to see what went wrong.  You can do this in VS Code by adding an entry to the “configurations” section of your launch.json file, in which you invoke gulp.js and pass a task name.

{
    "name": "Debug Gulp Task",
    "type": "node",
    "request": "launch",
    "program": "${workspaceRoot}/node_modules/gulp/bin/gulp.js",
    "stopOnEntry": false,
    "args": [
        // Replace with name of gulp task to run
        "imports:inject"
    ],
    "cwd": "${workspaceRoot}"
}

If you set a breakpoint in the “imports:inject” task, select “Debug Gulp Task” from the drop down in the Debug view in VS Code and press F5, it will launch the debugger and stop at the breakpoint you set.  You can then press F10 (step over) or F11 (step into), view local variables and add watches.

gulp-debug

Learning Gulp

If you would like to learn more about Gulp, I highly recommend John Papa’s Pluralsight course on Gulp, where he explains how to use Gulp to perform various build automation tasks, such as bundling, minification, versioning and integration testing. While the learning curve may appear steep at first, Gulp will make your life easier in the long run by automating repetitive tasks and allowing you to chain them together for a streamlined development workflow.

Posted in Technical | Tagged , | Leave a comment

Getting Visual Studio Code Ready for TypeScript: Part 2

Part 2: Writing Jasmine Tests in TypeScript

This is the second part in a series of blog posts on Getting Visual Studio Code Ready for TypeScript:

  1. Compiling TypeScript to JavaScript
  2. Writing Jasmine Tests in TypeScript (this post)

Jasmine vs Mocha + Chai + Sinon

jasmine.png

There are numerous JavaScript testing frameworks, but two of the most popular are Jasmine and Mocha.  I won’t perform a side-by-side comparison here, but the main difference is that Mocha does not come with built-in assertion and mocking libraries, so you need to plug in an assertion library, such as Chai, and a mocking library, such as Sinon.  Jasmine, on the other hand, includes its own API for assertions and mocks.  So if you want to keep things simple with fewer moving parts, and you don’t need extra features offered by libraries such as Chai and Mocha, Jasmine might be a more appealing option.  If, on the other hand, you want more flexibility and control, and you want features offered by dedicated assertion and mocking libraries, you might want to opt for Mocha.  For simplicity I’m going to stick with Jasmine in this blog post, but feel free to use Mocha if that better suits your purpose.

You can download a sample project with code for this blog post.  You can also download my Yeoman generator for scaffolding new TypeScript projects for Visual Studio Code.

Update: Since first publishing this blog post, I added a section on Debugging Jasmine Tests in VS Code, which you can find at the end of the article.

Using Jasmine with TypeScript

Jasmine is a behavior-driven development testing framework, which allows you to define test suites through one or more nested describe functions.  Each describe function accepts a string argument with the name of the test suite, which is usually the name of the class or method you are testing.  A test suite consists of one or more specs, formulated as a series of it functions, in which you specify expected behaviors.

Let’s say you have a Greeter class written in TypeScript, and it has a greet function.

namespace HelloTypeScript {
    export class Greeter {
        constructor(public message: string) {

        }
        greet(): string {
            return "Hello " + this.message;
        }
    }
}

Notice that Greeter is defined within the namespace HelloTypeScript and is qualified with the export keyword.  This removes Greeter from the global scope so that we can avoid potential name collisions.

To use Jasmine we’ll need to install jasmine-core (not jasmine) using npm (Node Package Manager).  Because we’re only using Jasmine at development-time, we’ll save it to the package.json file using the –save-dev argument.

npm install --save-dev jasmine-core

Intellisense for Jasmine

To allow Visual Studio Code to provide intellisense for Jasmine, you’ll need to install type definitions using Typings, which replaces the deprecated Tsd tool from Definitely Typed.

npm install -g typings

Use Typings to install type definitions for jasmine.

typings install jasmine --save-dev --ambient

This command will result in the addition of a typings folder in your project, which contains a main.d.ts file with references to installed type definitions.  The –save-dev argument will persist the specified typing as a dev dependency in a typings.json file, so that you can re-install the typings later.  The –ambient argument is required to include Definitely Typed in the lookup.

Now you’re ready to add your first Jasmine test.  By convention you should use the same name as the TypeScript file you’re testing, but with a .spec suffix.  For example, the test for greeter.ts should be called greeter.spec.ts and be placed in the same folder.

/// <reference path="../../typings/main.d.ts" />

describe("Greeter", () => {

    describe("greet", () => {

        it("returns Hello World", () => {

            // Arrange
            let greeter = new HelloTypeScript.Greeter("World");

            // Act
            let result = greeter.greet();

            // Assert
            expect(result).toEqual("Hello World");
        });
    });
});

The triple-slash reference is needed for intellisense to light up.  Without it you’ll see red squigglies, and VS Code will complain that it cannot find the name ‘describe’.

When you press Cmd+B to compile your TypeScript code, you will see a greeter.spec.js file in the dist/greeter directory, where the greeter.js file is also located.  You’ll also see a greeter.spec.js.map file to enable debugging of your Jasmine test.  (See the first post in this blog series for information on how to configure VS Code for compiling and debugging TypeScript.)

Running Jasmine Tests

To run your Jasmine tests in a browser, go to the latest release for Jasmine and download the jasmine-standalone zip file.  After extracting the contents of the zip file, copy both the lib folder and SpecRunner.html file to your project folder.  Edit the html file to include both the source and spec files.

<!-- include source files here... -->
<script src="dist/greeter/greeter.js"></script>

<!-- include spec files here... -->
<script src="dist/greeter/greeter.spec.js"></script>

You can then simply open SpecRunner.html in Finder (Mac) or File Explorer (Windows) to see the test results.

jasmine-file.png

If you change Greeter.greet to return “Goodbye” instead of “Hello”, then compile and refresh the browser, you’ll see that the test now fails.

jasmine-file-fail.png

Running Tests Automatically

Having to refresh the browser to see test results can become tedious, so it’s a good idea to serve your tests using http.  To help with this you can use a task runner such as Gulp, which integrates nicely with VS Code, together wish an http server such as BrowserSync.

gulp.png

First, you’ll want to install gulp and browser-sync locally.

npm install --save-dev gulp browser-sync

Next, add a gulpfile.js file to the project, in which you’ll define tasks for compiling TypeScript to JavaScript, as well as watching TypeScript files and recompiling them when there are changes.

var gulp = require('gulp');
var exec = require('child_process').exec;
var browserSync = require('browser-sync');

gulp.task('compile', function () {
    exec('rm -rf dist && tsc -p src');
});

gulp.task('watch', ['compile'], function () {
    return gulp.watch('./src/**/*.ts', ['compile']);
});

To run either the compile or watch tasks, we can execute them from a Terminal or Command Prompt.  (You can also run tasks in VS Code by pressing Cmd+P, typing “task ” [no quotes] and entering the task name).

gulp watch

gulp-watch.png

If you change a TypeScript file, the gulp watch task will detect the change and execute the compile task.  You can then add a gulp task which serves both .js and .spec.js files in a browser.

gulp.task('test', ['watch'], function () {

    var options = {
        port: 3000,
        server: './',
        files: ['./dist/**/*.js',
                './dist/**/*.spec.js',
                '!./dist/**/*.js.map'],
        logFileChanges: true,
        logLevel: 'info',
        logPrefix: 'spec-runner',
        notify: true,
        reloadDelay: 1000,
        startPath: 'SpecRunner.html'
    };

    browserSync(options);
});

Running Tests in VS Code

It is possible to wire up the gulp test task so that it runs in response to pressing Cmd+T.  To set up VS Code both for compiling TypeScript and running tests, press Cmd+Shift+P, type “config” and select Configure Task Runner. Replace the default content for tasks.json with the following:

{
    "version": "0.1.0",
    "command": "gulp",
    "isShellCommand": true,
    "args": [
        "--no-color"
    ],
    "tasks": [
        {
            "taskName": "compile",
            "isBuildCommand": true,
            "showOutput": "silent",
            "problemMatcher": "$gulp-tsc"
        },
        {
            "taskName": "test",
            "isTestCommand": true,
            "showOutput": "always"
        }
    ]
}

Pressing Cmd+B will compile your TypeScript files, and pressing Cmd+T will serve your Jasmine tests in a browser, automatically refreshing the browser each time any of your TypeScript files changes.

Using Modules

To improve encapsulation TypeScript supports the use of modules, which are executed in their own scope, not in the global scope.  Various constructs, such as variables, functions, interfaces and classes, are not visible outside a module unless they are explicitly exported.  For example, we could define an ItalianGreeter class with an export statement.

export default class ItalianGreeter {
    constructor(public message: string) {

    }
    greet(): string {
        return "Ciao " + this.message;
    }
}

The Jasmine test for ItalianGreeter would then require an import statement.

import ItalianGreeter from "./italiangreeter";

let greeter = new ItalianGreeter("World");

// Remaining code elided for clarity

To use modules in TypeScript you’ll need to specify a module loader in your tsconfig.json file.  For a TypeScript library or node.js app, you would select commonjs.

{
    "compilerOptions": {
    "module": "commonjs",

// Remaining code elided for clarity

At this point your TypeScript will compile, but the additional tests will not show up in SpecRunner.html, even after you include scripts for the source and spec files.  The reason is that you need SystemJs, which acts as a polyfill to provide support in the browser for module loading, which is a feature of ECMA Script 2015. First add systemjs to your project.

npm install --save-dev systemjs

Then add these two scripts to SpecRunner.html.

<script src="node_modules/systemjs/dist/system.js"></script>
<script>
    System.config({ packages: { 'dist': {defaultExtension: 'js'}}});
    Promise.all([
        System.import('dist/greeter/greeter.spec'),
        System.import('dist/italiangreeter/italiangreeter.spec'),
    ]);
</script>

Pressing Cmd+T will now also serve italiangreeter.spec.js, which imports the ItalianGreeter class.

tests-systemjs

Stopping Tests in VS Code

You can terminate the test task by pressing Cmd+Shift+P and selecting Terminate Running Task.  Because this is something you’ll do often, you might want to add a keyboard shortcut for it.  From the Code menu select Preferences / Keyboard Shortcuts, then add the following binding, which will terminate the running task by pressing Cmd+Shift+X.

[
    { "key": "shift+cmd+x", "command": "workbench.action.tasks.terminate" }
]

Debugging Tests in VS Code

While it may be useful to run Jasmine tests in a browser, there are times when you need to launch a debugger and step through your code one line at a time.  Visual Studio Code makes it relatively painless to debug your tests.  First you’ll need to install jasmine-node using npm.

npm install --save-dev jasmine-node

Then add the following entry to the “configurations” section of your launch.json file.

{
    "name": "Debug Tests",
    "type": "node",
    "request": "launch",
    "program": "${workspaceRoot}/node_modules/jasmine-node/bin/jasmine-node",
    "stopOnEntry": false,
    "args": [
        "dist",
        "--verbose"
    ],
    "cwd": "${workspaceRoot}",
    "sourceMaps": true,
    "outDir": "${workspaceRoot}/dist"
}

Press Cmd+Shift+D to view the Debugging pane in VS Code and select “Debug Tests” from the dropdown.  Then set a breakpoint (pressing F9 will do the trick), and press F5 to launch the debugger.  Execution should pause at the breakpoint, allowing you to step through your code.

debug-ts-tests.png

What’s Next?

In this post I showed how to write Jasmine tests in TypeScript and serve them in a browser by running a Gulp task either from the Terminal or in Visual Studio Code.  This has the advantage of automatically compiling TypeScript files and refreshing the browser whenever a source or spec file has changed.  While this works well at development time, you’ll need to use a test runner such as Karma if you want to execute tests on a continuous integration server when commits are pushed to a remote repository.  I’ll address this issue in my next post.

Posted in Technical | Tagged , | 1 Comment

Getting Visual Studio Code Ready for TypeScript

Part 1: Compiling TypeScript to JavaScript

This is the first part in a series of blog posts on Getting Visual Studio Code Ready for TypeScript:

  1. Compiling TypeScript to JavaScript (this post)
  2. Writing Jasmine Tests in TypeScript

Why TypeScript?

In case you’re new to TypeScript, Wikipedia defines TypeScript in the following way (paraphrased):

TypeScript is designed for development of large applications and transcompiles to JavaScript. It is a strict superset of JavaScript (any existing JavaScript programs are also valid TypeScript programs), and it adds optional static typing and class-based object-oriented programming to the JavaScript language.

Coming from a C# background, I was attracted to TypeScript, first because it is the brain child of Anders Hejlsberg, who also invented the C# programming language, and I can have confidence it has been well-designed, and second because I like to rely on the compiler to catch errors while I am writing code.  While TypeScript embraces all the features of ECMAScript 2015, such as modules, classes, promises and arrow functions, it adds type annotations which allow code editors to provide syntax checking and intellisense, making it easier to use the good parts of JavaScript while avoiding the bad.

You can download a sample project with code for this blog post.  You can also download my Yeoman generator for scaffolding new TypeScript projects for Visual Studio Code.

ts-logo.jpg

Why Visual Studio Code?

Once I decided to embark on the adventure of learning TypeScript, the next question was: What development tools should I use?

I’ve spent the better part of my career with Microsoft Visual Studio, and I enjoy all the bells and whistles it provides.  But all those fancy designers come at a cost, both in terms of disk space and RAM, and even installing or updating VS 2015 can take quite a while.  To illustrate, here is a joke I recently told a friend of mine:

I like Visual Studio because I can use it to justify to my company why I need to buy better hardware, so I can run VS and get acceptable performance. That’s how I ended up with a 1 TB SSD and 16 GB of RAM — thank you Visual Studio! 👏

I also own a MacBook Air, mainly because of Apple’s superior hardware, and run a Windows 10 virtual machine so that I can use Visual Studio and Office.  But I thought it would be nice to be able to write TypeScript directly on my Mac without having to spin up a Windows VM, which can drain my laptop’s battery.  So I thought I would give Visual Studio Code a try.

But before I started with VS Code, I decided to go back to Visual Studio and create a simple TypeScript project with support for unit testing with Jasmine, which is a popular JavaScript unit testing framework.  It turns out the experience was relatively painless, but I still had to do a lot of manual setup, which entailed creating a new TypeScript project in Visual Studio, deleting the files that were provided, installing NuGet packages for AspNet.Mvc and JasmineTest, then adding a bare-bones controller and a view which I adapted from the spec runner supplied by Jasmine.

You can download the code for a sample VS 2015 TypeScript project from my Demo.VS2015.TypeScript repository on GitHub.

Visual Studio 2015 still required me to do some work to create a basic TypeScript project with some unit tests, and if I wanted to add other features, such as linting my TypeScript or automatically refreshing the browser when I changed my code, then I would have to use npm or a task runner such as Grunt or Gulp. This helped tip the scales for me in favor of Visual Studio Code.

why-vs-code.png

VS Code is actually positioned as something between a simple code editor, such as Atom, Brackets or SublimeText, and a full fledged IDE like Visual Studio or WebStorm.  The main difference is that VS Code lacks a “File, New Project” command  for creating a new type of project with all the necessary files. This means you either have to start from scratch or select a Yeoman generator to scaffold a new project.

I decided to start from scratch, because I like pain. (OK, I’m just kidding.)

The truth is, I couldn’t find an existing generator that met my needs, and I wanted to learn all I could from the experience of getting VS Code ready for TypeScript.  The result was a sample project on GitHub (Demo.VSCode.TypeScript) and a Yeoman generator (tonysneed-vscode-typescript) for scaffolding new TypeScript projects.

Compiling TypeScript to JavaScript

My first goal was to compile TypeScript into JavaScript with sourcemaps for debugging and type definitions for intellisense.  This turned out to be much more challenging than I thought it would be.  I discovered that the gulp-typescript plugin did not handle relative paths very well, so instead I relied on npm (Node Package Manager) to invoke the TypeScript compiler directly, setting the project parameter to the ‘src’ directory in which I placed my tsconfig.json file.  This allowed for specifying a ‘dist’ output directory and preserving the directory structure in ‘src’.  To compile TypeScript using a gulp task, all I had to do was execute the ‘tsc’ script.

/**
 * Compile TypeScript
 */
gulp.task('typescript-compile', ['vet:typescript', 'clean:generated'], function () {

    log('Compiling TypeScript');
    exec('node_modules/typescript/bin/tsc -p src');
});

Here is the content of the ‘tsconfig.json’ file. Note that both ‘rootDir’ and ‘outDir’ must be set in order to preserve directory structure in the ‘dist’ folder.

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "sourceMap": true,
        "declaration": true,
        "removeComments": true,
        "noImplicitAny": true,
        "rootDir": ".",
        "outDir": "../dist"
    },
    "exclude": [
        "node_modules"
    ]
}

Debugging TypeScript

I could then enable debugging of TypeScript in Visual Studio Code by adding a ‘launch.json’ file to the ‘.vscode’ directory and including a configuration for debugging the currently selected TypeScript file.

{
    "name": "Debug Current TypeScript File",
    "type": "node",
    "request": "launch",
    // File currently being viewed
    "program": "${file}",
    "stopOnEntry": true,
    "args": [],
    "cwd": ".",
    "sourceMaps": true,
    "outDir": "dist"
}

Then I could simply open ‘greeter.ts’ and press F5 to launch the debugger and break on the first line.

vsc-debugger.png

Linting TypeScript

While compiling and debugging TypeScript was a good first step, I also wanted to be able to lint my code using tslint.  So I added a gulp task called ‘vet:typescript’ and configured my ‘typescript-compile’ task to be dependent on it.  The result was that, if I for example removed a semicolon from my Greeter class and compiled my project from the terminal, I would see a linting error displayed.

lint-error.png

Configuring the Build Task

I also wanted to be able to compile TypeScript simply by pressing Cmd+B.  That was easy because VS Code will use a Gulpfile if one is present.  Simply specify ‘gulp’ for the command and ‘typescript-compile’ for the task name, then set ‘isBuildCommand’ to true.

{
    "version": "0.1.0",
    "command": "gulp",
    "isShellCommand": true,
    "args": [
        "--no-color"
    ],
    "tasks": [
        {
            "taskName": "typescript-compile",
            "isBuildCommand": true,
            "showOutput": "always",
            "problemMatcher": "$gulp-tsc"
        }
    ]
}

Adding a Watch Task

Lastly, I thought it would be cool to run a task that watches my TypeScript files for changes and automatically re-compiles them.  So I added yet another gulp task, called ‘typescript-watch’, which first compiles the .ts files, then watches for changes.

/**
 * Watch and compile TypeScript
 */
gulp.task('typescript-watch', ['typescript-compile'], function () {

    return gulp.watch(config.ts.files, ['typescript-compile']);
});

I could then execute this task from the command line. Here you can see output shown in the terminal when a semicolon is removed from a .ts file.

tsc-watch.png

It is also possible to execute a gulp task from within VS Code.  Press Cmd+P, type ‘task’ and hit the spacebar to see the available gulp tasks.  You can select a task by typing part of the name, then press Enter to execute the task.

vscode-tasks.png

Using a Yeoman Generator

While it’s fun to set up a new TypeScript project with Visual Studio Code from scratch, an easier way is to scaffold a new project using a Yeoman generator, which is the equivalent of executing File, New Project in Visual Studio.  That’s why I built a Yeoman generator called tonysneed-vscode-typescript, which gives you a ready-made TypeScript project with support for unit testing with Jasmine and Karma.  (I’ll explain more about JavaScript testing frameworks in the next part of this series.)

yeoman-logo.png

To get started using Yeoman, you’ll need to install Yeoman with the Node Package Manager.

npm install -g yo

Next install the tonysneed-vscode-typescript Yeoman generator.

npm install -g generator-tonysneed-vscode-typescript

To use the generator you should first create the directory where you wish to place your scaffolded TypeScript project.

mkdir MyCoolTypeScriptProject
cd MyCoolTypeScriptProject

Then simply run the Yeoman generator.

yo tonysneed-vscode-typescript

To view optional arguments, you can append –help to the command.  Another option is to skip installation of npm dependencies by supplying an argument of –skip-install, in which case you can install the dependencies later by executing npm install from the terminal.

In response to the prompt for Application Name, you can either press Enter to accept the default name, based on the current directory name, or enter a new application name.

yo-ts-vsc-typescript.png

Once the generator has scaffolded your project, you can open it in Visual Studio Code from the terminal.

code .

After opening the project in Visual Studio Code, you will see TypeScript files located in the src directory.  You can compile the TypeScript files into JavaScript simply by pressing Cmd+B, at which point a dist folder should appear containing the transpiled JavaScript files.

For the next post in this series I will explain how you can add unit tests to your TypeScript project, and how you can configure test runners that can be run locally as well as incorporated into your build process for continuous integration.

Posted in Technical | Tagged , , | 11 Comments