Angular2 Gulp, Typescript, NodeJS and Express
Introduction
Basic Angular seed application created with Quick start application given on angular website (https://angular.io/docs/ts/latest/quickstart.html). It uses Typescript, Gulp for tasks like build, compile, install etc and ExpressJS as server. The application basically involve both client(Angular2) and server(Express and Node).
Github Directory - https://github.com/moizKachwala/angular2-express
Getting Started
Before we start make sure you have NodeJS installed in your machine.If not then you can download and install NodeJS from (https://nodejs.org/en/)
You need to install typings globally by following command.
npm install --global typings
Project Setup
Create a new directory and install dependencies. This will create a folder and will add the package.json in the folder.
mkdir Angular2-express
cd Angular2-express
npm init -y
Now Add dependencies to your package.json.
"dependencies": {
"angular2": "^2.0.0-beta.17",
"es6-promise": "^3.2.1",
"es6-shim": "^0.35.1",
"express": "^4.13.4",
"gulp-concat": "^2.6.0",
"reflect-metadata": "^0.1.2",
"rxjs": "^5.0.0-beta.6",
"systemjs": "^0.19.29",
"zone": "^0.3.4"
}
And your dev dependencies.
"devDependencies": {
"concurrently": "^2.1.0",
"del": "^2.2.0",
"gulp": "^3.9.1",
"gulp-concat": "^2.6.0",
"gulp-nodemon": "^2.1.0",
"gulp-sourcemaps": "^1.6.0",
"gulp-tslint": "^4.3.3",
"gulp-typescript": "^2.13.6",
"gulp-typings": "^2.0.0",
"run-sequence": "^1.2.1",
"tslint": "^3.5.0"
}
Now install all the dependencies by opening the created folder in command prompt and fire following command.
npm install
Once you have done npm install
, you will find a node_modules folder in your directory. This holds all the dependencies that we added in package.json. Now create a file gulpfile.ts and two folders in your project client and server.
Our folder structure will look something like this.
angular2-express
|-- node_modules
|-- client
|-- server
gulpfile.ts
package.json
Now as we have taken gulpfile.ts we need to tell how to compile the ts file by adding tsconfig.json. This also set the outDir which we will be handling later. We have also added exclude option so we don't want typescript to go and compile those directives/files.
{
"compilerOptions": {
"outDir": "build/app",
"target": "es5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules",
"typings",
"typings/index.d.ts"
]
}
So now we have done with the basic structure we will start implementing Server for our application.
Server Setup
First thing we need to do is create a tsconfig.json inside server folder.This will tell our typescript compiler how to compile our .ts extension files.
Let's create tsconfig.json in /server folder with following content.
{
"compilerOptions": {
"target": "ES5",
"module": "commonjs",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"typings"
]
}
In our server directory, We need to add typings.json. Typings is the simple way to manage and install TypeScript definitions.
{
"globalDependencies": {
"express": "registry:dt/express#4.0.0+20160317120654",
"express-serve-static-core": "registry:dt/express-serve-static-core#0.0.0+20160602151406",
"mime": "registry:dt/mime#0.0.0+20160316155526",
"node": "registry:dt/node#6.0.0+20160608083720",
"serve-static": "registry:dt/serve-static#0.0.0+20160606155157"
}
}
Now go to command prompt, and navigate to server folder and execute the following command. This will install the typescript definations for our server.
typings install
You will notice that it created a directory typings inside /server directory
Now lets create server.ts inside /server directory.
import express = require('express');
import path = require('path');
var port: number = process.env.PORT || 3000;
var app = express();
app.use('/app', express.static(path.resolve(__dirname, 'app')));
app.use('/libs', express.static(path.resolve(__dirname, 'libs')));
var renderIndex = (req: express.Request, res: express.Response) => {
res.sendFile(path.resolve(__dirname, 'index.html'));
}
app.get('/*', renderIndex);
var server = app.listen(port, function() {
var host = server.address().address;
var port = server.address().port;
console.log('This express app is listening on port:' + port);
});
- The server will run on port 3000
- We have made app and lib folders as static content folder.
- All the routes will be served by index.html
- app folder will hold all the files related to angular2 and libs folder will hold all the dependencies. Don't worry we will create this folders in some time.
We are done with the server side work. Our directory structure will look like this.
angular2-express
|-- node_modules
|-- client
|-- server
|-- typings
|-- server.ts
|-- tsconfig.json
|-- typings.json
gulpfile.ts
package.json
tsconfig.json
Client Setup
Let's drive the Client Side Application with Angular2.
In /client directory add index.html
<html>
<head>
<title>Angular 2 Quickstart with Express</title>
<base href="/"/>
<!-- 1. Load libraries -->
<script src="libs/angular2/bundles/angular2-polyfills.js"></script>
<script src="libs/systemjs/dist/system.src.js"></script>
<script src="libs/rxjs/bundles/Rx.js"></script>
<script src="libs/angular2/bundles/angular2.dev.js"></script>
<script src="libs/angular2/bundles/router.dev.js"></script>
<!-- 2. Configure SystemJS -->
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('app/main')
.then(null, console.error.bind(console));
</script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
- This is the basic startup file for the angular application
- Notice System.config in index.html. This will tell angular to load module based scripts in the browser.
- There are libs included in the file which will load some of the core dependencies for our angular application.
As we are developing our application in Typescript, we need to add tsconfig.json
{
"compilerOptions": {
"target": "ES5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"typings"
]
}
We will also need some typings to be installed for our application. Add typings.json like we added for server
{
"globalDependencies": {
"es6-shim": "registry:dt/es6-shim#0.31.2+20160602141504"
}
}
Once you have added typings.json we need to install typings for our client application. Navigate to client folder in command prompt and run following command.
typings install
Angular2 Code
Now let's start adding our angular code. create a directory named app inside /client folder.
Note : I am just going to cover the basic angular code from quick start application. The main agenda for this post is to use angular with Express server. Please go to my github to get the finished angular Quick start application.
Now in /client/app, Create our first component app.component.ts. This is our root component for our application
import {Component} from 'angular2/core';
@Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
export class AppComponent { }
Now we need to add a bootstrap class which will initialize our application. Add main.ts inside /app directory.
import {bootstrap} from 'angular2/platform/browser';
import {AppComponent} from './app.component';
bootstrap(AppComponent);
Till this point our project structure should look like this.
angular2-express
|-- node_modules
|-- client
|-- app
|-- app.component.ts
|-- main.ts
|-- typings
|-- index.html
|-- tsconfig.json
|-- typings.json
|-- server
|-- typings
|-- server.ts
|-- tsconfig.json
|-- typings.json
gulpfile.ts
package.json
tsconfig.json
GulpFile (Task Management)
Now we have our client and server setup we need to add a root file which would clean, compile and build our application. Modify gulpfile.ts
"use strict";
const gulp = require("gulp"),
del = require("del"),
tsc = require("gulp-typescript"),
sourcemaps = require('gulp-sourcemaps'),
tsProject = tsc.createProject("tsconfig.json"),
tslint = require('gulp-tslint'),
concat = require('gulp-concat'),
runSequence = require('run-sequence'),
nodemon = require('gulp-nodemon'),
gulpTypings = require("gulp-typings");
/**
* Remove build directory.
*/
gulp.task('clean', (cb) => {
return del(["build"], cb);
});
/**
* Build Express server
*/
gulp.task('build:server', function () {
var tsProject = tsc.createProject('server/tsconfig.json');
var tsResult = gulp.src('server/**/*.ts')
.pipe(sourcemaps.init())
.pipe(tsc(tsProject))
return tsResult.js
.pipe(concat('server.js'))
.pipe(sourcemaps.write())
.pipe(gulp.dest('build'))
});
gulp.task('build:client', function(){
var tsProject = tsc.createProject('client/tsconfig.json');
var tsResult = gulp.src('client/**/*.ts')
.pipe(sourcemaps.init())
.pipe(tsc(tsProject))
return tsResult.js
.pipe(sourcemaps.write())
.pipe(gulp.dest('build'))
});
/**
* Lint all custom TypeScript files.
*/
gulp.task('tslint', () => {
return gulp.src("client/app/**/*.ts")
.pipe(tslint())
.pipe(tslint.report('prose'));
});
/**
* Compile TypeScript sources and create sourcemaps in build directory.
*/
gulp.task("compile", ["tslint"], () => {
let tsResult = gulp.src("client/**/*.ts")
.pipe(sourcemaps.init())
.pipe(tsc(tsProject));
return tsResult.js
.pipe(sourcemaps.write("."))
.pipe(gulp.dest("build"));
});
/**
* Copy all resources that are not TypeScript files into build directory.
*/
gulp.task("resources", () => {
return gulp.src(["client/**/*", "!**/*.ts"])
.pipe(gulp.dest("build"));
});
/**
* Copy all required libraries into build directory.
*/
gulp.task("libs", () => {
return gulp.src([
'angular2/bundles/angular2-polyfills.js',
'systemjs/dist/system.src.js',
'rxjs/bundles/Rx.js',
'angular2/bundles/angular2.dev.js',
'angular2/bundles/router.dev.js'
], {cwd: "node_modules/**"}) /* Glob required here. */
.pipe(gulp.dest("build/libs"));
});
/**
* Watch for changes in TypeScript, HTML and CSS files.
*/
gulp.task('watch', function () {
gulp.watch(["client/**/*.ts"], ['compile']).on('change', function (e) {
console.log('TypeScript file ' + e.path + ' has been changed. Compiling.');
});
gulp.watch(["client/**/*.html", "client/**/*.css"], ['resources']).on('change', function (e) {
console.log('Resource file ' + e.path + ' has been changed. Updating.');
});
});
/**
* Install typings for server and client.
*/
gulp.task("installTypings",function(){
var stream = gulp.src(["./server/typings.json","./client/typings.json"])
.pipe(gulpTypings(null)); //will install all typingsfiles in pipeline.
return stream; // by returning stream gulp can listen to events from the stream and knows when it is finished.
});
/**
* Start the express server with nodemon
*/
gulp.task('start', function () {
nodemon({ script: 'build/server.js'
, ext: 'html js'
, ignore: ['ignored.js']
, tasks: ['tslint'] })
.on('restart', function () {
console.log('restarted!')
});
});
/**
* Build the project.
* 1. Clean the build directory
* 2. Build Express server
* 3. Build the Angular app
* 4. Copy the resources
* 5. Copy the dependencies.
*/
gulp.task("build", function (callback) {
runSequence('clean', 'build:server', 'build:client', 'resources', 'libs', callback);
});
- Gulpfile handles many tasks.
- Clean - clean the build directory
- Compile - compile the .ts files and generate .js and .map files.
- Build:server - Build our server.ts
- Build:client - Build our angular application.
- Watch - Watch for the changes.
- Start - It uses nodemon for running the server. The benefit of running this is it will automatically restart the server when there is any change in the code.
- tslint - this will run the code against some best practices.
Running the code
npm install
npm build
npm start
Ts Lint
Create a file tslint.json in the root directory.
{
"rules": {
"class-name": true,
"curly": true,
"eofline": false,
"forin": true,
"indent": [
true,
4
],
"label-position": true,
"label-undefined": true,
"max-line-length": [
true,
200
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-key": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-eval": true,
"no-string-literal": false,
"no-trailing-whitespace": true,
"no-unused-variable": false,
"no-unreachable": true,
"no-use-before-declare": true,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"radix": true,
"semicolon": true,
"triple-equals": [
true,
"allow-null-check"
],
"variable-name": false,
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-separator"
]
}
}
The final project structure will look like this.
angular2-express
|-- node_modules
|-- client
|-- app
|-- app.component.ts
|-- main.ts
|-- typings
|-- index.html
|-- tsconfig.json
|-- typings.json
|-- server
|-- typings
|-- server.ts
|-- tsconfig.json
|-- typings.json
gulpfile.ts
package.json
tsconfig.json
tslint.json
Finally
Stay tuned for adding mongoDB support in this application.