Angular 2 First Look (Hello World!)

Tags: Angular

Last week Google announced the final release of Angular 2. I'm not one for messing around with the alphas and RCs but now it's time to take a close look. In this post I'm going to walk through the steps to create a bare bones project. It will contain a single component but no pipes, directives, or anything more advanced. I owe Dan Wahlin and John Papa a shout out here because I studied code they checked into their GitHub repos in order to get to this point myself.

Let's first setup our environment. You can follow along with these steps or clone the project from GitHub.

Install the latest version of Code or Atom. In this post I'll use Code because I'm coming from a VS IDE but feel free to use Atom if that's your editor. I plan to walk through these steps on my own with Atom as well.

Next install the latest version of Node.js (v6.x) which gives you the Node Package Manager (npm) as well.

Open a command prompt and test your installation:

	node -v
	npm -v

Now create a folder somewhere called "helloworld" (lowercase). For example mine is at:

c:\sandbox\angular2\helloworld

Open the folder with Code. Open the Extensions Ctrl + Shift + X) and install John Papa's Angular 2 code snippets. They will save you tons of typing and make sure your components follow the Angular style guide.

Our environment is setup. Before we create the app let me highlight a major difference between Angular 2 and its predecessor. Angular 1.x was an MVC design and your project was organized around models, views, and controllers. In Angular 2 this is no longer the case. Angular 2 is organized mainly around components. The best practice is to structure your app around features. You create a folder for each feature and place all of the presentation and logic together in one place. In this walkthrough we're going to create a single component called "home" which is our home page.

But before we get to the home component we've got to configure the app plumbing and setup the project. Create these five files in the root helloworld folder:

	package.json
	tsconfig.json
	typings.json
	bs-config.json
	jsconfig.json

Paste this code in each of the files:

package.json

{
  "name": "angular-quickstart",
  "version": "1.0.0",
  "scripts": {
    "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
    "lite": "lite-server",
    "postinstall": "typings install",
    "tsc": "tsc",
    "tsc:w": "tsc -w",
    "typings": "typings"
  },
  "license": "ISC",
  "dependencies": {
    "@angular/common": "2.0.0",
    "@angular/compiler": "2.0.0",
    "@angular/core": "2.0.0",
    "@angular/forms": "2.0.0",
    "@angular/http": "2.0.0",
    "@angular/platform-browser": "2.0.0",
    "@angular/platform-browser-dynamic": "2.0.0",
    "@angular/router": "3.0.0",
    "@angular/upgrade": "2.0.0",
    "core-js": "^2.4.1",
    "reflect-metadata": "^0.1.3",
    "rxjs": "5.0.0-beta.12",
    "systemjs": "0.19.27",
    "zone.js": "^0.6.23",
    "angular2-in-memory-web-api": "0.0.20",
    "bootstrap": "^3.3.6"
  },
  "devDependencies": {
    "concurrently": "^2.2.0",
    "lite-server": "^2.2.2",
    "typescript": "^2.0.2",
    "typings":"^1.3.2"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
  }
}

typings.json

{
  "globalDependencies": {
    "core-js": "registry:dt/core-js#0.0.0+20160725163759",
    "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
    "node": "registry:dt/node#6.0.0+20160909174046"
  }
}

bs-config.json

{
  "server": {
    "baseDir": [
      "./src/",
      "./"
    ]
  }
}

jsconfig.json

{
    // See http://go.microsoft.com/fwlink/?LinkId=759670
    // for the documentation about the jsconfig.json format
    "compilerOptions": {
        "target": "es5"
    },
    "exclude": [
        "node_modules",
        "bower_components",
        "jspm_packages",
        "tmp",
        "temp"
    ]
}

Now that the dependencies are defined in our package.json file we can install them in the project. Open a command prompt in the local folder or a terminal in Code (Ctrl + `) and type

npm install

to install them. When it's finished you should see a

node_modules

folder in the project root folder. Now we have the Angular framework referenced in our project.

I mentioned earlier that components are to be organized in their own subfolders. The Angular style guide illustrates this structure:

Create a subfolder called src in the root and then two folders app and styles beneath that. In the src folder create two files and paste the following markup into each one:

	index.html
	systemjs.config.js

index.html

<!DOCTYPE html>
<html>
<head>
    <base href="/">
    <title>Angular 2 Hello World</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="Angular 2 Hello World">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
    <link href="styles/styles.css" rel="stylesheet" />
    
    <!-- Polyfill(s) for older browsers -->
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script src="systemjs.config.js"></script>
    <script>
        System.import('app').catch(function(err){ console.error(err); });
    </script>  
</head>
<body>
    <header class="navbar navbar-inner navbar-fixed-top">
        <nav class="container">
            <div class="navbar-header">
              <span class="app-title">Angular 2 Hello World</span>
            </div>
        </nav>
    </header>

    <main class="container">
        <app-container>
            Loading...
        </app-container>
        <br /><br />
    </main>

    <footer>
        <div class="navbar navbar-fixed-bottom">
            <div class="navbar-inner footer">
                <div class="container">
                    <footer>
                        
                    </footer>
                </div>
            </div>
        </div>
    </footer>
</body>
</html>

systemjs.config.js

(function(global) {
  
  // map tells the System loader where to look for things
  var map = {
    'app':                        'app', // 'dist',
    '@angular':                   'node_modules/@angular',
    'rxjs':                       'node_modules/rxjs'
  };

  // packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app': { 
      main: 'main.js',  
      defaultExtension: 'js' 
    },
    'rxjs': { 
      defaultExtension: 'js' 
    }
  };

  var ngPackageNames = [
    'common',
    'compiler',
    'core',
    'forms',
    'http',
    'platform-browser',
    'platform-browser-dynamic',
    'router',
    'upgrade',
  ];

  // Individual files (~300 requests):
  function packIndex(pkgName) {
    packages['@angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' };
  }

  // Bundled (~40 requests):
  function packUmd(pkgName) {
    packages['@angular/' + pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' };
  }

  // Most environments should use UMD; some (Karma) need the individual index files
  var setPackageConfig = System.packageWithIndex ? packIndex : packUmd;
  // Add package entries for angular packages
  ngPackageNames.forEach(setPackageConfig);

  var config = {
    map: map,
    packages: packages
  };

  System.config(config);

})(this);

Before we move on to the app folder create styles.css in the styles folder and use this basic style sheet:

html {
    overflow-y: scroll;
    overflow-x: hidden;
}

main {
    position: relative;
    padding-top: 60px;
}

/* Ensure display:flex and others don't override a [hidden] */
[hidden] { display: none !important; }

.navbar-inner {
    padding-left: 0px;
    -webkit-border-radius: 0px;
    border-radius: 0px;
    -webkit-box-shadow: none;
    -moz-box-shadow: none;
    box-shadow: none;
    background-color: #648364;
    background-image: none;
}

.navbar-inner.toolbar {
    background-color: #fafafa;
}

footer {
    margin-top: 15px;
}

.navbar-inner.footer {
    background-color: #fafafa;
    -webkit-box-shadow: none;
    -moz-box-shadow: none;
    box-shadow: none;
    height:50px;
}

.nav.navBarPadding {
    margin-left:25px;
    margin-top: 10px;
}

.navbar .brand {
    margin-top: 2px;
    color: #fff;
    -webkit-text-shadow: none;
    text-shadow: none;
}

.navbar-toggle {
  border: 1px solid white;
}

.navbar-toggle .icon-bar {
  background-color: white;
}

.app-title {
    line-height:50px;
    font-size:20px;
    color: white;
}

Now we need to configure the app bootstrapper which requires these four files in the app folder:

	app.component.ts
	app.module.ts
	main.ts
        app.routing.ts

app.component.ts

import { Component } from '@angular/core';

@Component({ 
  selector: 'app-container',
  template: `<router-outlet></router-outlet>`
})

export class AppComponent {
  constructor() { } 
}

app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule} from '@angular/http';

import { AppComponent }  from './app.component';
import { HomeComponent } from './home/home.component';
import { AppRouting } from './app.routing';

@NgModule({
  imports:      [ BrowserModule, FormsModule, HttpModule, AppRouting ],
  declarations: [ AppComponent, HomeComponent ],
  providers:    [ ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { enableProdMode } from '@angular/core';

//enableProdMode(); //Uncomment for production
platformBrowserDynamic().bootstrapModule(AppModule)  
  .then((success: any) => console.log('App bootstrapped'))
  .catch((err: any) => console.error(err));

app.routing.ts

import { RouterModule, Routes } from '@angular/router';

import { HomeComponent }     from './home/home.component';

const routes: Routes = [
  { path: '',  pathMatch:'full', redirectTo: '/home' },
  { path: 'home',  component: HomeComponent }
];

export const AppRouting = RouterModule.forRoot(routes);

This takes care of bootstrapping the application itself. Now we can finally add our home component. Under app create a folder called home. Create a file in home called home.component.ts. Now we get to try out the Angular 2 code snippets. Open the home.component.ts file and on line 1 type "ng2" and some intellisense should pop up with various recipes like in this screenshot:

You want the ng2-controller snippet. Press ENTER and you have the code snippet in the file. Replace the word selector and feature with home.

The code should look like this:

import { Component, OnInit } from '@angular/core';

@Component({
    moduleId: module.id,
    selector: 'home',
    templateUrl: 'home.component.html'
})
export class HomeComponent implements OnInit { 
    constructor() { }
    ngOnInit() { }
}

Notice that templateUrl refers to the file home.component.html that does not yet exist. Create it now in the same folder and paste in some simple markup:

<h1>Welcome to the home page!</h1>

We're done. Open up the command terminal again and type

npm start

to run the script that starts the app in lite-server. (The "start" script was configured in the package.json file.) You should see the home page: