MEAN App with Angular 2 and Webpack
This is a basic tutorial on setting up an Angular 2 project. While the Angular CLI makes creating new projects a breeze, understanding the Angular 2 ecosystem still takes some getting used to. In this tutorial, we'll demonstrate the basics of Angular 2. We'll show you how to set up a bare-bone example using the MEAN stack and Webpack without any ng tooling magic.
The purpose of this tutorial is to dissect the different components of the Angular 2 framework. This is not intended to demonstrate the fastest possible way of getting to "Hello World". Rather, this exercise is intended to help you understand the framework as a whole before jumping into Angular CLI, etc.
Installing Your Dependencies
We are going to use Node.js and the MEAN stack to create our project. Create a new folder for your project and add the following package.json file.
{
"name": "angular-quickstart",
"version": "1.0.0",
"description": "Angular 2 QuickStart with Webpack",
"author": "",
"license": "",
"dependencies": {
"@angular/common": "^4.0.0",
"@angular/compiler": "^4.0.0",
"@angular/core": "^4.0.0",
"@angular/platform-browser": "^4.0.0",
"@angular/platform-browser-dynamic": "^4.0.0",
"core-js": "^2.4.1",
"rxjs": "^5.1.0",
"zone.js": "^0.8.4",
"webpack": "^3.2.0",
"typescript": "*",
"ts-loader": "*"
"express": "*"
}
}
Now in the root directory of the project, run:
npm install
This should install all of the dependencies you need for the project in a node_modules folder. Here is a an explanation of each dependency and how it serves the Angular 2 framework.
@angular/common
This Angular module holds many of the common functions and directives that you'll be working with in Angular 2. This includes things likeNgIf, NgFor, and NgClass.
@angular/compiler
The compiler is a core feature of Angular 2. It's what allows you to use custom HTML tags and directives within the DOM. The compiler makes it possible to attach events to DOM elements and is central to making Angular work with your markup.
@angular/core
The core module is similar to @angular/common in that it includes global services specific to the Angular framework. You will find the Angular definition of classes and other constructs in this module.
@angular/platform-browser
This module is for bootstrapping your Angular app to run in the browser. This is a required module if you want to run your Angular app in the browser.
@angular/platform-browser-dynamic
Similar to @angular/platform-browser, this module is responsible for bootstrapping your Angular app so it can run in browsers.
core-js
core-js is a modular standard library for JavaScript. It's purpose is to make JavaScript easier to read and understand. It includes browser fallbacks (or polyfills) and is required for Angular 2. Specifically the shim.js library is useful for translating only partially supported ES6 features into more widely accepted ES5.
rxjs
RxJS is another crucial JavaScript library for Angular 2. RxJS handles asynchronous data streams through observables. It's been around for awhile but handles the asynchronous activity of your web app in a much more elegant fashion than more traditional async libraries. For a more in-depth explanation, check out this great article we found on Medium.
zones.js
Zones.js is another crucial component to Angular 2. It's essentially an improvement on Angular 1's digest cycle and allows individual components to update independently on change events. This is a huge performance upgrade from Angular 1 and gives Angular 2 some resemblance to React and flux architecture.
webpack
You may or may not be familiar with Webpack. If you are and Webpack is globally installed, you don't need to include this as a dependency for the project.
Webpack does a bunch of great stuff for asset management but we will be using it mainly as a transpiler. Since Angular 2 uses TypeScript, we need to transpile our TypeScript files to JavaScript so we can reference them as JavaScript files in the browser.
typescript
Angular 2 is written with TypeScript. TypeScript is a superset of JavaScript that brings type checking and extensibility to the language. It takes some getting used to but can save you time and increase performance as it forces good practice.
ts-loader
Webpack requires loaders to transpile your code. You can think of loaders as "tasks" or build tools that transform languages from one to another. Since we want to transpile our TypeScript files to JavaScript, we require the ts-loader so we can use it with Webpack. If this sounds confusing, don't worry. We will be visiting Webpack in more detail later on in this tutorial.
express
Express is a lightweight web application framework for Node.js. It puts the "E" in MEAN and provides an easy way to spin up local web servers.
Configuring TypeScript
Now that you've downloaded all of your dependencies with npm install it's time to configure TypeScript for your project. You want to place a tsconfig.json file at the root of your project and give it the following contents:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true
}
}
The tsconfig.json specifies the configuration options for your TypeScript instance. For example, when the sourceMap option is set to true TypeScript will generate corresponding .map files for each ts extension. For the purposes of this tutorial, we will not be covering the configuration attributes in detail. Just remember that this is the configuration file for your TypeScript instance.
Configuring Webpack
We use Webpack to take an input file and transpile it into a browser friendly JavaScript version. What goes in as TypeScript comes out as JavaScript. In the root directory of the project, add a webpack.config.js file and give it the following contents:
module.exports = {
entry: "./main.ts",
output: {
path: __dirname,
filename: "main.js"
},
module: {
loaders: [
{ test: /.ts$/, loader: "ts-loader" }
]
},
resolve: {
extensions: ['.js' , '.webpack.js', '.web.js', '.ts']
}
};
In our webpack.config.js file we specify our main entry (or input) file. We then specify the desired output file main.js.
We include the ts-loader so webpack knows to transpile our files using a TypeScript loader. In the resolve clause we specify file extensions to transpile (as some of our referenced files won't have extensions).
In the root directory, create a index.html file. This will be the parent page for our app. Give it the following contents:
<!DOCTYPE html>
<html>
<head>
<title>Angular QuickStart</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Polyfill(s) for older browsers -->
<script src="scripts/core-js/client/shim.min.js"></script>
<script src="scripts/zone.js/dist/zone.js"></script>
</head>
<body>
<my-app>Loading AppComponent content here ...</my-app>
</body>
<script src="/main.js"></script>
</html>
Notice how we are referencing the main.js file at the bottom of our HTML doc. This is the output script that we configured for webpack. Whenever we run webpack, it will take our Angular 2 code and convert it to the main.js bundle file for our app.
Configuring Express
Our app will run locally using Express. To set up a basic Express server, copy the following into a index.js file in your root directory:
const express = require('express')
const app = express()
app.use('/scripts', express.static(__dirname + '/node_modules/'));
app.use('/', express.static(__dirname + '/'));
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
This is a basic Express server that's configured to run locally on port 3000. Notice how we've defined a basic GET route'/' that points to our index.html file.
It's also worth noting that we've specified a static directory so we can actually reference our node_modules in our index.html file.
Getting Started With Angular 2
Now that we have our environment set up, it's time to actually have some fun with Angular 2. Remember how the webpack.config.js referenced a main.ts file? Let's create that now. In the root directory of the project, create a main.ts file with the following contents:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
Every Angular 2 app is bootstrapped to a root app module. In our main.ts file, we import the necessary modules to bootstrap our Angular 2 app to a specified /app/app.module. Let's create that file now.
Creating the root module
We want to include all of our Angular 2 code under a central app. Create a new folder called app. Inside your newly created folder, create a app.module.ts file with the following contents:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
This creates the root AppModule for our project. It is important to note that the @NgModule decorator function takes a metadata object that dictates how Angular should compile and run. Also notice how we reference a app.component file. Let's create that now.
Creating the first component
In the app directory create a app.component.ts file with the following:
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: "<h1>Hello {{name}}</h1>"
})
export class AppComponent { name = 'Bob'; }
Components are the most basic building blocks for Angular 2 apps. In the @Component decorator, we define the HTML selector and template. This is very similar to directives in Angular. We then define the actual AppComponent class. You can think of this as the controller if your coming from Angular 1. In this class definition we've simply defined a single name property but you can build it out with different properties and methods, connect to services, etc.
This illustrates one of the key advantages of Angular 2. With the @Component annotation, we clearly separate the component's functionality from it's rendering logic. This is what makes cross-platform app development possible with Angular 2. The classes or components we write can serve different view engines with the same implementation.
Running the app
Transpile the code
It's time to bring our app to life. If you have Webpack installed globally, run:
webpack
Otherwise, you can run webpack within the node_modules directory of the project. Navigate to the root folder and run:
node node_modules/webpack/bin/webpack
Starting Express
The final step is to start the Express server we set up in index.js. Navigate to the root directory and run:
node index
This will start the local Express server on port 3000. If all goes according to plan, you should be able to navigate to localhost:3000 and see "Hello Bob".
Conclusion
It may seem like a lot at first but once you get past TypeScript using Angular 2 isn't that bad. Yes there is a lot of configuration involved but also remember that we took the long way in order to better understand how the Angular 2 ecosystem works.
By using Angular's CLI, you can more efficiently create components on the fly and bypass a lot of the TypeScript configuration steps. You can also use other npm build tools to enable hot reloads for even faster development.