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.
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
- 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 +-- @email@example.com +-- @firstname.lastname@example.org +-- @email@example.com +-- @firstname.lastname@example.org +-- @email@example.com +-- @firstname.lastname@example.org +-- @email@example.com +-- @firstname.lastname@example.org `-- @email@example.com
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 > firstname.lastname@example.org 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:
tsc server/*.ts --watch
npm run backend which calls
nodemon --watch server ./server/bin/www
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.jshas been converted to
app.tsbut it still uses
routes/api.jsthat 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
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
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.
Go to day 11 here.