100 days of Angular 2 - Day 10: Update Angular and Remove Typings

Yesterday I tried to update the back end (which is just three files of Express.js related code) from JavaScript to TypeScript.

I then went to build the front end (the Angular 2 code) and everything went to hell. Depending on the environment I used to build the code with Webpack, it failed for different reasons.

On the server

90% optimize assets 
Error in bail mode: [default] /home/stephen/apps/100-days-of-angular2/client/app.component.ts:5:14 
Cannot find name 'require'.

and in my local environment

90% optimize assets 
Error in bail mode: [default] C:\Users\stephen\git\100-days-of-angular2\client\day-001-vegetables\vegetable.service.ts:30:22
Cannot find name 'Promise'.

I'm guessing that because I'm getting different TypeScript Type information from two sources (one from the npm @types packages, the other from typings.json) a component of the build process is getting confused.

Goodbye typings

The Angular 2 documentation and quickstart doesn't use the typings.json file anymore, so my plan of attack for today is as follows:

  • Update Angular from 2.0.1 to 2.2.0
  • Remove the typings dependency and the typings.json file
  • Install the TypeScript type information using npm @types

To update Angular, I just incremented the version in my package.json and then ran an npm install.

$ npm install
+-- @angular/common@2.2.1
+-- @angular/compiler@2.2.1
+-- @angular/core@2.2.1
+-- @angular/forms@2.2.1
+-- @angular/http@2.2.1
+-- @angular/platform-browser@2.2.1
+-- @angular/platform-browser-dynamic@2.2.1
+-- @angular/router@3.2.1
`-- @angular/upgrade@2.2.1

Next I deleted the typings dependency and the typings.json file. I didn't run npm uninstall or anything, since once I commit my changes I'll eventually wipe out the entire project directory, perform a git clone and a new npm install to confirm I have a consistent environment down the road.

Then I ran the following two commands

$ npm install @types/core-js --save-dev
$ npm install @types/node --save-dev

I could have copied the versions that the Angular Quickstart recoomends, but I wanted to which version would show up in my package.json relative to what the quickstart uses. There was just a patch version difference for @types/node so I was happy with that.

Will it work?

Well, time to give it a shot. Here's what I got trying to perform the Webpack build.

$ npm run build

> angular-quickstart@1.0.0 build C:\Users\stephen\git\100-days-of-angular2
> rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail
                                                843366ms1163ms optim141ms emit
Hash: 7d3462b07dfb3b3c8530
Version: webpack 1.13.3
Time: 16339ms
                                Asset       Size  Chunks             Chunk Names
        app.7d3462b07dfb3b3c8530.js    99.3 kB       0  [emitted]  app
    polyfills.7d3462b07dfb3b3c8530.js    97.9 kB       1  [emitted]  polyfills
    vendor.7d3462b07dfb3b3c8530.js     925 kB       2  [emitted]  vendor
    app.7d3462b07dfb3b3c8530.js.map     558 kB       0  [emitted]  app
polyfills.7d3462b07dfb3b3c8530.js.map     740 kB       1  [emitted]  polyfills
vendor.7d3462b07dfb3b3c8530.js.map    6.08 MB       2  [emitted]  vendor
                        index.html  539 bytes          [emitted]
    + 637 hidden modules

WARNING in app.7d3462b07dfb3b3c8530.js from UglifyJs
Condition always true [./~/angular2-template-loader!./client/main.ts:7,4]
Condition always true [./~/@angular/forms/bundles/forms.umd.js:7,0]

WARNING in polyfills.7d3462b07dfb3b3c8530.js from UglifyJs
Condition always true [./~/angular2-template-loader!./polyfills.ts:4,4]
Dropping unreachable code [./~/angular2-template-loader!./polyfills.ts:8,2]
Condition always true [./~/zone.js/dist/zone.js:9,0]
Non-strict equality against boolean: == false [./~/zone.js/dist/zone.js:1394,0]
... etc 
Dropping unreachable code [./~/@angular/router/bundles/router.umd.js:825,0]
Declarations in unreachable code! [./~/@angular/router/bundles/router.umd.js:825               ,0]
Child html-webpack-plugin for "index.html":
        + 1 hidden modules
$

Looks promising. Can I get everything running in my development environment? Remember, that's three terminals:

  1. tsc server/*.ts --watch
  2. npm run backend which calls nodemon --watch server ./server/bin/www
  3. npm start which calls webpack-dev-server --inline --progress --port 8085

In picture form it looks like this:

It works! Hooray!

Looks removing typings was a good idea. Now I need to make sure things look good in a production environment. I'll commit my changes and push to GitHub. On the server (one of my virtual machines that's running 24/7), I'll follow these steps:

git clone https://github.com/netinstructions/100-days-of-angular2 100-days-take-3
cd 100-days-take-3
npm install
npm run build
export NODE_ENV=production
tsc server/*.ts
forever start --uid="100days" --append ./server/bin/www

Note that I clone the project into a brand new directory (named 100-days-take-3 since it's my third attempt). I want to make sure I have a reproducible build an deployment process and that any lingering dependencies or files are not hanging around.

A quick note - I use forever to keep the node.js process running for forever. I give it a process ID and also tell it to append to the existing log file located at ~/.forever/100days.log which is left over from running the app previously.

All of the commands above worked perfectly on the server. Hooray! I gave the website a quick smoke test, clicking around and making sure things worked, and am happy with the results.

I think I'll take a break soon. There's a few things left to do down the road.

  • Simplify the development process. Running three different commands in three different terminals is less than ideal.
  • Finish up the server side conversion to TypeScript. Currently only app.js has been converted to app.ts but it still uses routes/api.js that could be converted to TypeScript.
  • Start exploring Angular Universal.

One of the things I want to figure out is whether Angular Universal will solve one of my routing problems. Check out this scenario:

  1. Visitor arrives at the root of the site. In other words, the browser requests index.html which loads Angular and everything else like app.[hash].js.
  2. The user then clicks on a link, such as /tour-of-vegetables. Angular 2 routing detects this and displays that page. No network requests actually occur to switch to that page since it already was bundled with app.[hash].js. The URL looks like http://100daysofangular2.internetuproar.com/tour-of-vegetables

Contrast that to a different scenario:

  1. The user enters in http://100daysofangular2.internetuproar.com/tour-of-vegetables to the browser's address bar. In other words, the browser requests index.html from a route such as /tour-of-vegetables which isn't defined on the server in the Express app.
  2. User receives a 404 not found.

It's pretty easy to show by just navigating to a linked page and refreshing the page.

There's a nice discussion on URL styles with Angular 2 and on StackOverflow here, but the HashBang approach isn't the appropriate solution for me, since it doesn't preserve the option to do server-side rendering at a later point. The default HTML 5 style produces URLs that are easier for users to understand as well.

I'll need to see how or if Angular Universal has any solutions to this, since I'd like to have server side rendering anyways for other reasons (that I'll discuss tomorrow) and the server will need to be made aware of the routes and URLs in order to achieve that.

You can see the full code on GitHub here and the live website here, since it's up and running again.

Go to day 11 here.