Lesson Monday

If you run into an error concerning polyfills when trying to build your app, use this command to install a package that makes sure they are supported:

> npm install promise-polyfill --save-exact

Now that we have a basic understanding of TypeScript, the client-side MVC structure, and the benefits of Angular, we're ready to start developing our first Angular application!

In the next two lessons, we'll walk through everything necessary to get a basic Angular app up and running. This lesson will focus on setting up tools we're already familiar with such as npm, Bower, Gulp, and TypeScript. The next lesson will walk through the new, Angular-specific setup steps.

index.html

First, let's create a project directory named angular2-to-do. (Or, if you're revisiting this lesson to set up a specific application, use a name relevant to that project).

We'll begin by creating an index.html file in the top level of our directory. It should contain the following:

index.html
<html>
  <head>
    <title>Angular 2 To Do List for Epicodus</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="build/js/vendor.min.js"></script>
    <link rel="stylesheet" href="build/css/vendor.css">
    <!-- 1. Load libraries -->
     <!-- 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>
    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>

    <link rel="stylesheet" href="build/css/styles.css">
    <script>
      System.import('app').catch(function(err){ console.error(err); });
    </script>
  </head>
  <!-- 3. Display the application -->
  <body>
    <app-root>Loading...</app-root>
  </body>
</html>

This code is directly from the Angular 2 documentation and is the recommended way to start a project. Let's walk through what each part is responsible for:

  1. <!-- 1. Load libraries → This is a placeholder for third-party JavaScript libraries we may later include. It will remain empty for now.

  2. <!-- Polyfill(s) for older browsers →. This section is where any polyfills will reside. A polyfill is code that implements a feature on web browsers that don't technically support that feature. This code compensates for any issues older browsers may experience while running an Angular application. The polyfills included here point to Angular dependencies in the node_modules directory. We'll configure and install npm dependencies a little later.

  3. <!-- 2. Configure SystemJS → The script included here is a .js script file that allows SystemJS to load modules compiled using ourTypeScript compiler. SystemJS is a module loader tool. Angular uses it internally to construct our applications.

  4. You'll also notice the following JavaScript embedded in <script> tags directly in the HTML: System.import('app').catch(function(err){ console.error(err); }); This code helps Angular start up an application. It tells the SystemJS tool to use the source code in the app folder as an entry point to launch our application.

  5. We've included the familiar <script> and <link> tags for loading vendor files for our CSS And JavaScript dependencies, too:

<script src="build/js/vendor.min.js"></script>
<link rel="stylesheet" href="build/css/vendor.css">

NPM Dependencies

Let's gather our npm dependencies next. Create a package.json file in the top-level of the project directory. Then, add the following dependencies:

package.json
{
  "name": "angular2-to-do",
  "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"
  },
  "license": "MIT",
  "dependencies": {
    "@angular/common": "2.4.0",
    "@angular/compiler": "2.4.0",
    "@angular/core": "2.4.0",
    "@angular/forms": "2.4.0",
    "@angular/http": "2.4.0",
    "@angular/platform-browser": "2.4.0",
    "@angular/platform-browser-dynamic": "2.4.0",
    "@angular/router": "3.4.0",
    "@angular/upgrade": "2.4.0",
    "bootstrap": "3.3.6",
    "angular-in-memory-web-api": "0.3.1",
    "core-js": "2.4.1",
    "reflect-metadata": "0.1.3",
    "rxjs": "5.0.1",
    "zone.js": "0.7.2",
    "systemjs": "0.19.27"
  },
  "devDependencies": {
    "bower-files": "3.11.3",
    "browser-sync": "2.11.1",
    "del": "2.2.0",
    "gulp": "3.9.1",
    "gulp-concat": "2.6.0",
    "gulp-sass": "2.2.0",
    "gulp-shell": "0.5.2",
    "gulp-sourcemaps": "1.6.0",
    "gulp-uglify": "1.5.3",
    "gulp-util": "3.0.7",
    "concurrently": "3.0.0",
    "lite-server": "2.2.2",
    "typescript": "2.2.2",
    "typings":"1.3.2"
  }
}

Similar to index.html, this is also from the official Angular documentation (with the addition of packages for our Gulp tasks) and is the recommended way to begin an Angular project.

Let's walk through the packages and dependencies this file will import into our project.

The very first section includes the name and version number of our application.

...
  "name": "angular2-to-do",
  "version": "1.0.0",
...

We'll call ours angular2-to-do because we'll transform this basic project into a To Do List in upcoming lessons.

Next, the "scripts" section...

...
"scripts": {
    "start": "tsc && concurrently \"npm run tsc:w\" \"npm run lite\" ",
    "lite": "lite-server",
    "postinstall": "typings install",
    "tsc": "tsc",
    "tsc:w": "tsc -w"
  },
...

This section includes configurations for TypeScript.

The dependencies section lists the project's dependencies.

...
"dependencies": {
    "@angular/common": "2.4.0",
    "@angular/compiler": "2.4.0",
    "@angular/core": "2.4.0",
    "@angular/forms": "2.4.0",
    "@angular/http": "2.4.0",
    "@angular/platform-browser": "2.4.0",
    "@angular/platform-browser-dynamic": "2.4.0",
    "@angular/router": "3.4.0",
    "@angular/upgrade": "2.4.0",
    "bootstrap": "3.3.6",
    "angular-in-memory-web-api": "0.3.1",
    "core-js": "2.4.1",
    "reflect-metadata": "0.1.3",
    "rxjs": "5.0.1",
    "zone.js": "0.7.2",
    "systemjs": "0.19.27"
  },
...

Most of these are Angular files. Importing them allows us access to the tools and capabilities of the framework.

The devDependencies section outlines the packages and tools that are required while we develop our project but don't need to be included for the production version to run.

...
  "devDependencies": {
    "bower-files": "3.11.3",
    "browser-sync": "2.11.1",
    "del": "2.2.0",
    "gulp": "3.9.1",
    "gulp-concat": "2.6.0",
    "gulp-sass": "2.2.0",
    "gulp-shell": "0.5.2",
    "gulp-sourcemaps": "1.6.0",
    "gulp-uglify": "1.5.3",
    "gulp-util": "3.0.7",
    "concurrently": "3.0.0",
    "lite-server": "2.2.2",
    "typescript": "2.2.2",
    "typings":"1.3.2"
  }
...

You'll probably recognize the majority of these; we used them in week 1 while creating Gulp tasks.

Bower Dependencies

Eventually we'll also use Bower to manage our front-end dependencies. Let's add it to the project now.

You can install Bower globally if it isn't already installed on your current machine:

$ npm install bower -g

(Note: Epicodus machines already have Bower installed, and you likely already installed it on your own machine while following along with previous lessons. You can run the command $ npm list -g --depth=0 to see what npm packages are installed on your machine. If Bower is already installed, you do not need to run this command again.)

If you have permissions errors with this step, use $ sudo npm install bower -g and enter your password.

Next, we can initialize Bower in our project to create the manifest file:

$ bower init

This will prompt you to provide information about your project. You can press the Enter key after each prompt to accept the default settings.

Whenever we need to add a front-end dependency, we can use the $ bower install package-name --save command just as we've done in previous weeks.

.gitignore

We don't need to commit files downloaded by npm or Bower to our Git and GitHub repositories. We only need our package.json and bower.json manifest files that list the project's dependencies. If anyone wants to download and run our project, they can run $ npm install and $ bower install to retrieve their own local versions of dependencies. For this reason, we should always ignore the dependency files.

We'll create a .gitignore file in the top-level of our project directory, and include the following:

.gitignore
node_modules/
npm-debug.log
bower_components/
app/*.js
app/*.js.map
.DS_Store
build/

Resources

We'll eventually want to include images and styles in our project, too. Let's create a folder in the top-level of the directory called resources. Inside it we'll create subdirectories called images, styles, and js. At this point, your project directory should look like this:

angular2-to-do
├── bower.json
├── index.html
├── package.json
└── resources
    ├── images
    ├── js
    └── styles
  • images will eventually contain any pictures we want in our app.

  • js will contain any JavaScript our app may use that is not part of the Angular framework.

Next, we'll add a styles.css file to the resources/styles folder. We can later add SASS files here, too.

TypeScript Configuration

Next we need a file called tsconfig.json in the top level of our project folder. It will configure the TypeScript compiler:

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": [ "es2015", "dom" ],
    "noImplicitAny": false,
    "suppressImplicitAnyIndexErrors": true
  }
}

Gulp Tasks

Lastly, we'll create our gulpfile.js:

gulpfile.js
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');

var lib = require('bower-files')({
  "overrides":{
    "bootstrap" : {
      "main": [
        "less/bootstrap.less",
        "dist/css/bootstrap.css",
        "dist/js/bootstrap.js"
      ]
    }
  }
});

var utilities = require('gulp-util');
var buildProduction = utilities.env.production;
var del = require('del');
var browserSync = require('browser-sync').create();
var shell = require('gulp-shell');
var sass = require('gulp-sass');
var sourcemaps = require('gulp-sourcemaps');

////////////////////// TYPESCRIPT //////////////////////


gulp.task('tsClean', function(){
  return del(['app/*.js', 'app/*.js.map']);
});

gulp.task('ts', ['tsClean'], shell.task([
  'tsc'
]));

////////////////////// BOWER //////////////////////


gulp.task('jsBowerClean', function(){
  return del(['./build/js/vendor.min.js']);
});

gulp.task('jsBower', ['jsBowerClean'], function() {
  return gulp.src(lib.ext('js').files)
    .pipe(concat('vendor.min.js'))
    .pipe(uglify())
    .pipe(gulp.dest('./build/js'));
});

gulp.task('cssBowerClean', function(){
  return del(['./build/css/vendor.css']);
});

gulp.task('cssBower', ['cssBowerClean'], function() {
  return gulp.src(lib.ext('css').files)
    .pipe(concat('vendor.css'))
    .pipe(gulp.dest('./build/css'));
});

gulp.task('bower', ['jsBower', 'cssBower']);

////////////////////// SASS //////////////////////

gulp.task('sassBuild', function() {
  return gulp.src(['resources/styles/*'])
    .pipe(sourcemaps.init()) 
    .pipe(sass())
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('./build/css'));
});

////////////////////// SERVER //////////////////////


gulp.task('serve', ['build'], function() {
  browserSync.init({
    server: {
      baseDir: "./",
      index: "index.html"
    }
  });
  gulp.watch(['resources/js/*.js'], ['jsBuild']); // vanilla js changes, reload.
  gulp.watch(['*.html'], ['htmlBuild']); // html changes, reload.
  gulp.watch(['resources/styles/*.css', 'resources/styles/*.scss'], ['cssBuild']);      gulp.watch(['app/*.ts'], ['tsBuild']); // typescript files change, compile then reload.
});

gulp.task('jsBuild', function(){
  browserSync.reload();
});

gulp.task('htmlBuild', function(){
  browserSync.reload();
});

gulp.task('cssBuild', ['sassBuild'], function(){
  browserSync.reload();
});

gulp.task('tsBuild', ['ts'], function(){
  browserSync.reload();
});

////////////////////// GLOBAL BUILD TASK //////////////////////

gulp.task('build', ['ts'], function(){
  // we can use the buildProduction environment variable here later.
  gulp.start('bower');
  gulp.start('sassBuild');
});

This is the same as the gulpfile we used during the first week of class. To review Gulp, gulpfiles, or any of the specific tasks seen above, revisit lessons in the Introduction to JavaScript section of this course's curriculum.

Conclusion

We won't be able to launch our fledgling application in the browser just yet! We still have work to do. In the next lesson, we'll walk through the new Angular-specific setup required to get an application up and running.


Example GitHub Repo for Angular 2 To Do List

Terminology


  • Polyfill: Code that implements a feature on web browsers that don't technically support that feature. This code compensates for any issues older browsers may experience while running an application.

  • SystemJS: A module loader tool used internally by Angular to construct our applications.

Tips


  • Always ignore your dependency files (ie: node_modules and bower_components directories) from your GitHub repo by adding them to your .gitignore file.

Examples


Example GitHub Repo for Angular 2 To Do List