Angular 2 is all the rage, and for good reason. The core team moved its language dependencies to Typescript, the framework now has a more modular focus thanks to components, and like v1 the framework is batteries-included, so there's no need to opt into side frameworks that handle things like data binding or rendering.

Once its up and running, it makes front-end development fun again.

But what I found out when trying to bootstrap an application from scratch was that getting the app working might be the most difficult part. For some reason the front-end JS community has chosen to use a collection of build tools that don't always work as advertised. You have webpack, SystemJS, jspm, along with the usual suspects Gulp and Grunt.

So which ones should you use? I chose to go with webpack because it takes care of graphing dependencies and concatenation out of the box. There are other reasons to go with webpack too, read more about it here.

So let's get started. The official Angular 2 docs are all in Typescript (correction: now they have a vanilla JS version as well) so I opted to use Typescript in my project as well. Even if TS isn't mandatory, it pays to have a full fledged type system so that as your project grows the code stays readable.

Write out your package.json:

{
  "name": "angular2-scratch",
  "version": "1.0.0",
  "description": "angular2 proj from scratch",
  "main": "app.js",
  "scripts": {
    "build": "bash setup.sh",
    "test": "test"
  },
  "keywords": [
    "angular2"
  ],
  "author": "Prakash Venkatraman",
  "license": "ISC",
  "dependencies": {
    "@angular/common": "2.0.0-rc.4",
    "@angular/compiler": "2.0.0-rc.4",
    "@angular/core": "2.0.0-rc.4",
    "@angular/forms": "0.2.0",
    "@angular/http": "2.0.0-rc.4",
    "@angular/platform-browser": "2.0.0-rc.4",
    "@angular/platform-browser-dynamic": "2.0.0-rc.4",
    "@angular/router": "3.0.0-beta.1",
    "@angular/router-deprecated": "2.0.0-rc.2",
    "@angular/upgrade": "2.0.0-rc.4",
    "angular2-in-memory-web-api": "0.0.14",
    "angular2-materialize": "^3.0.3",
    "angular2-template": "0.0.1",
    "autoprefixer": "^6.3.7",
    "bootstrap": "^3.3.6",
    "core-js": "^2.4.0",
    "hammerjs": "^2.0.8",
    "imports-loader": "^0.6.5",
    "jquery": "^2.2.4",
    "materialize-css": "^0.97.7",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.6",
    "typescript": "^1.8.10",
    "webpack": "^1.13.1",
    "zone.js": "^0.6.12"
  },
  "devDependencies": {
    "ts-loader": "^0.8.2",
    "webpack": "^1.13.1",
    "webpack-dev-server": "^1.14.1",
    "typings": "^1.0.4"
  }
}

Let's take a second to talk about what's going on here. All of the libraries prefixed with @angular are core library dependencies. Why are they handled by npm? Webpack builds its dependency graph using the node_modules folder as its point of reference. This will make more sense we build our first component.

You'll notice deps like typescript, rxjs, and zone.js. These are libraries that angular2 depends on. Why aren't they bundled into the core library? Go figure. I spent atleast an hour debugging to figure out that these were required. Now you don't have to.

Typescript requires both a configuration file as well as type definitions. So we'll need a tsconfig.json file for the first and a typings.json file for the second.

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  },
  "exclude": [
    "node_modules"
  ]
}
typings.json
{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160602141332",
    "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
    "node": "registry:dt/node#6.0.0+20160621231320",
    "zone.js": "github:DefinitelyTyped/DefinitelyTyped/zone.js/zone.js.d.ts#9027703c0bd831319dcdf7f3169f7a468537f448"
  }
}

Then to get everything installed run

npm install  
typings install  

To make sure the type definitions are properly installed, be sure to install the typings installer. It should have been taken care of in the first npm installation, but just case the command is not recognized, run npm install typings -g.

Now that we have our dependencies installed, its time to setup webpack.

Webpack

Webpack is a bundling tool that transpiles and concatenates typescript code with other javascript libraries. The config file needs 3 blocks: a set of entrypoints, an output path, and a set of module loaders used to transform the input.

First make a file called webpack.config.json in the root:

var path = require('path');

module.exports = {}  

then add a set of entry points:

module.exports = {  
entry: [  
    './node_modules/zone.js/dist/zone.js',
    './node_modules/jquery/dist/jquery.min.js',
    './node_modules/materialize-css/dist/js/materialize.min.js',
    './src/polyfills.ts',
    './src/app/main.ts']
}

Don't worry about the polyfill and main typescript files just yet, we'll be making those later.

Add an output destination:

module.exports = {  
entry: [  
    './node_modules/zone.js/dist/zone.js',
    './node_modules/jquery/dist/jquery.min.js',
    './node_modules/materialize-css/dist/js/materialize.min.js',
    './src/polyfills.ts',
    './src/app/main.ts'],
output: {  
    path: path.resolve('./dist'),
    filename: 'bundle.js'
  },
}

This will transpile, concatenate, and copy all files in your project to the destination <webpack_config_path>/dist/bundle.js.

To make sure webpack operates on the files you want, you will need to add module loaders for each file type:

module.exports = {  
module: {  
    loaders: [
      {
        test: /\.tsx?$/,
        loaders: ['ts']
      },
      {
        test: /\.html$/,
        loader: 'html'
      },
      {
        test: /\.css$/,
        loaders: ['style', 'css']
      }
    ]
   }
}

With that you should be good to go. Here is the full file for comparison.

Typescript

Now we have to add project files. Make a directory called src/app/:

mkdir -p src/app/  

Inside this directory, add a file called app.ts. Create a basic Angular 2 component. We're going to add a component that uses Materialize css:

import { Component } from "@angular/core";  
import { MaterializeDirective } from "angular2-materialize";

@Component({
    selector: "my-component",
    directives: [MaterializeDirective],
    template: `
    <!-- Modal Trigger -->
    <div class="waves-effect btn modal-trigger">Modal</div>
    <!-- Modal Structure -->
    <div id="modal1" class="modal">
      <div class="modal-content">
        <h4>Modal Header</h4>
        <p>A bunch of text</p>
      </div>
      <div class="modal-footer">
        <a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">Agree</a>
      </div>
    </div>
      `
})
export class AppComponent{}  

This is a component that will button on the page. Once the button is clicked, a Materialize modal will popup on the screen.

Now we have to bootstrap the AppComponent. Make a file called src/app/main.ts:

import { bootstrap } from "@angular/platform-browser-dynamic";  
import { AppComponent } from "./app";

bootstrap(AppComponent);  

The bootstrap function binds the App Component to angular2's internal module loader.

Since angular2 requires certain polyfills to work properly, we will need to load them via webpack. Make a file called src/polyfill.ts:

import "reflect-metadata";  

(Eventually this file will get longer.)

Now all that's left is to make an html page to serve. Create an index page at src/index.html:

<html>  
  <head>
    <link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
    <link href="../node_modules/materialize-css/dist/css/materialize.css" rel="stylesheet" />
  </head>
  <body>
    <my-component></my-component>
    <script src="./bundle.js"></script>
  </body>
</html>  

Beautiful! This file assumes that the bundle file produced by webpack will be a sibling to this file. What I did was write a bash script for copying the indexfile:

# <project_root>/setup.sh
webpack --progress;  
cp -v ./src/index.html ./dist/index.html;  

And then I added this script to my package.json scripts block:

...
"scripts": {
    "build": "bash setup.sh",
    "test": "test"
  },
...

Now if you run npm run build it will run webpack and copy your index file. If you open the ./dist/index.html file, you should see a button on the screen that says "Modal". Click on it and see if the modal appears!

Find the full project code here. Feel free to fork the project!