Angular 2 is still in Alpha, however it can be used today, and it’s a good idea to get a jump start on the future to give you that competitive edge. If you are experienced at all with Angular 1.x then some of the concepts such as Directives, Controllers, and Services will be a little familiar to you. However, Angular 2 is completely different than the first. Many of the concepts such as modules and providers have been for the most part replaced with (future) native JavaScript implementations. This is one reason that the Angular team chose to rewrite everything from the ground up, keeping focused on the future of JavaScript (EcmaScript) in mind from the very start.
Normally when developing web applications, I would say that you can use whatever IDE / text editor that you want. While that is technically still true, in this case I’m going to specifically recommend that you use Visual Studio Code. A free cross platform editor developed by Microsoft, I believe that this is going to be the best tool primarily because of Intellisense.
You’ll also need to have node.js and npm installed and be comfortable with the basics. If you aren’t up to speed with node, you can check out my beginner’s article on the subject or this video course at Zenva Academy.
BUILD GAMES
FINAL DAYS: Unlock 250+ coding courses, guided learning paths, help from expert mentors, and more.
Tutorial Source Code
All of the source code for this tutorial can be downloaded here: source code .
Quick Primer on TypeScript and ES6
TypeScript, much like ES6, is a superset of JavaScript and adds features while leaving the original language there. It has a compiler / transpiler that produces JavaScript, it was created by Microsoft and is also open source. It is the core language being used to write Angular 2, and the recommended path for writing applications in Angular 2. The compiler comes pre-installed with Visual Studio, or you can grab it as a node package.
npm install -g typescript
Statically Typed Variables
In TypeScript, variables can (and should be) strongly typed. It doesn’t have any bearing on code execution, because it’s still just JavaScript, but what it does give you is a way for the compiler to let you know about mistakes and hopefully avoid side effects. The available basic types are: boolean, number, string, array, enum, any, and void. You can also create custom types. Check the handbook for further details.
function foo(bar: string, baz: number) { return bar + baz; } // ... foo('boo', 'beep'); // compiler error!
Classes
Classes in TypeScript follow the ES6 standard for classes. JavaScript doesn’t have true classes like traditional OOP languages, rather they are simply sugar to help make inheritance easier. Classes provide a clean way to declare a constructor, methods and variable members. TypeScript further enhances them by providing the notion of scoping and static typing. Within the end result JavaScript there is still no such thing as a private variable, however the compiler can check and throw errors to warn you about improper usage. Classes can also be subclassed using the extends keyword, and automatically give you the super method for utilizing the implementation of the parent.
class Animal { public name: string; public sound: string; private feet: number; constructor(name: string, sound: string, feet: number) { this.name = name; this.sound = sound; this.feet = feet; } speak() { console.log('The ' + this.name + ' says ' + this.sound); } } class Dog extends Animal { constructor() { super('dog', 'bark', 4); } } var fido: Dog = new Dog(); fido.speak();
In order to accomplish this in ES5 the code is a lot less clean and readable, though it still can be done using the standard way to inherit from a prototype in JavaScript.
function Animal(name, sound, feet) { this.name = name; this.sound = sound; this.feet = feet; } Animal.prototype.speak = function() { console.log('The ' + this.name + ' says ' + this.sound); }; function Dog() { Animal.call(this, 'Dog', 'bark', 4); } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; var fido = new Dog(); fido.speak();
Interfaces
An interface defines a contract of methods and variables that must be implemented by a class or be the signature for a variable type. A class can be designated to have an interface using the implements keyword. Interfaces can extend each other similar to classes. If the contract is not fulfilled the compiler will provide errors to warn you.
interface IVector2D { x: number; y: number; } interface IVector3D extends IVector2D { z: number; } interface WorldObject { position: IVector3D; rotation: IVector3D; scale: IVector3D; } class Vector implements IVector3D { constructor(public x: number, public y: number, public z: number) { } add(otherVector: Vector) { this.x += otherVector.x; this.y += otherVector.y; this.z += otherVector.z; } toJSON() { var x = this.x, y = this.y, z = this.z; return { x, y, z }; } } class GameObject implements WorldObject { position: Vector; rotation: Vector; scale: Vector; name: string; constructor(name: string) { this.name = name; this.position = new Vector(0, 0, 0); this.rotation = new Vector(0, 0, 0); this.scale = new Vector(1, 1, 1); } translate(amount: Vector) { this.position.add(amount); } } var foo = new GameObject('Fooman Choo'); console.log(foo.position); // {x: 0, y: 0, z: 0} foo.translate(new Vector(2, 3, 0)); console.log(foo.position); // {x: 2, y: 3, z: 0}
Decorators
Decorators are actually a feature slated for ES7, and so they are still listed as experimental within TypeScript. They are however, super cool, and Angular 2 is making use of them so they are important to understand. There are four types of decorators: Class, Method, Property, and Parameter. Most often you will encounter Class decorators while working with Angular.
A class decorator is a function that takes the class (constructor) as an argument, and it allows us to modify that constructor before each instance is created. If the decorator function returns a function, that function will be the new constructor.
In the following example “Secured” is the function that will be used as a decorator. Decorators are applied using the @ sign and placed on top of the object they are decorating. This demonstrates the power of decorators being that at design time we can configure classes to have a specific behavior. We could have instead set the allowedRoles property on the instance after it was created, but that would need to happen at runtime. We could have also instead created a subclass or separate class for each with hard coded defaults. Using decorators this way is a much more componentized approach.
enum UserRoles { 'None' = 0, 'Registered' = 1 << 0, 'Admin' = 1 << 1, 'Moderator' = 1 << 2 } function Secured(roles: UserRoles[]) { return function(target: any) { target.prototype.allowedRoles = roles; } } class User { roles: UserRoles = UserRoles.None; constructor(public name: string, roles: UserRoles[]) { let r = UserRoles.None; roles.forEach(function(role) { r |= role; }); this.roles = r; } } class BaseService { name: string = 'BaseService'; allowedRoles: UserRoles[]; constructor() {} protected isAuthorized(user: User): boolean { let allowed = this.allowedRoles.every(function(role) { return ((user.roles & role) === role); }); return allowed; } securedMethod(user: User): any { return 'foo'; } } @Secured([UserRoles.Moderator, UserRoles.Admin]) class ServiceA extends BaseService { name: string = 'ServiceA'; securedMethod(user: User): any { let allowed = this.isAuthorized(user); if (!allowed) { throw new Error(user.name + ' failed the security check!'); } return 'highly classified data'; } } @Secured([UserRoles.Registered]) class ServiceB extends BaseService { name: string = 'ServiceA'; securedMethod(user: User): any { let allowed = this.isAuthorized(user); if (!allowed) { throw new Error(user.name + ' failed the security check!'); } return 'normal user data'; } } var normalUser = new User('Bob', [UserRoles.Registered]); var adminUser = new User('Steve', [UserRoles.Registered, UserRoles.Moderator, UserRoles.Admin]); var serviceA = new ServiceA(); var serviceB = new ServiceB(); function access(user: User, service: BaseService): void { try { console.log(user.name + ' accessed ' + service.name + ' and found ' + service.securedMethod(user)); } catch (e) { console.error(e.message); } } access(adminUser, serviceA); access(normalUser, serviceA); access(adminUser, serviceB); access(normalUser, serviceB); /* Steve accessed ServiceA and found highly classified data Bob failed the security check! Steve accessed ServiceA and found normal user data Bob accessed ServiceA and found normal user data */
The actual decorator function is the inner function in the Secured method. In order to provide configuration to the decorator (which takes a specific signature) we can wrap it as we did in a factory function returning the decorator method and gaining access to the outer method’s scope.
One other thing that you may not be familiar with in the above example is the use of enum . Enumerations are a concept found in many other languages and are basically a special type of Object. Check the TypeScript handbook on this subject for more details.
The second type of decorators modify properties of an object. Property decorators take the prototype of the object being decorated, and the key of the property. An example of a property decorator is the @validate decorator below. We can setup a custom getter and setter to perform validation based on validation method passed in.
function validate(validator: Function) { return function(target: any, prop: string) { var val = this[prop]; Object.defineProperty(target, prop, { enumerable: true, configurable: true, get: function() { return val; }, set: function(value) { if (validator(value)) { val = value; console.info('Validation success!', value); } else { console.error('Validation failed! ', value); } } }) }; } class Foo { @validate((value) => value > 0) count: number; } var foo = new Foo(); foo.count = 3; // OK foo.count = -1; // ERROR!
While TypeScript will validate that the type of the variable count is in fact a number, it won’t be able to validate the business rules. As you can see, this function is simply modifying the prototype of Foo and adding custom getter and setter based on the passed in callback method. This is also something that can be achieved in plain JavaScript, however adding a simple validate statement above the property is much cleaner and very readable.
The syntax for the callback method that I’ve used is ES6 “fat arrow” function syntax. You can read more about this in the documentation, however basically it creates a function with arguments on the left, and the return value on the right of the “fat arrow”.
Methods can have decorators as well, you pass in the function being decorated, the name of the function, and its descriptor.
function log(target: Function, key: string, descriptor: any) : MethodDecorator { var originalMethod = descriptor.value; descriptor.value = function (...args: any[]) { var a = args.map(a => JSON.stringify(a)).join(); var result = originalMethod.apply(this, args); var r = JSON.stringify(result); console.log(`Call: ${key}(${a}) => ${r}`); return result; } return descriptor; } class Foo { @log add(a: number, b: number): number { return a + b; } } var foo = new Foo(); foo.add(1, 2);
While you can create your own decorators, you probably aren’t going to need to at least at first, but now you will have a better understanding of what is going on in the Angular 2 code.
Modules
There are several different choices for doing modules in ES5: require.js, amd, system, etc. Even Angular 1 had its own module system in place to enable you to namespace things easily across multiple files and not pollute the global namespace.
In TypeScript modules can be both internal and external. Internal modules are created using the module keyword. They are “internal” to TypeScript as in they compile to function closures rather than using some other module system such as AMD or CommonJS.
module foo { export var bar: number = 32; } module baz { var bing: number = 88; export var bar: number = 77; export function log(): void { console.log('inside: ', bing, bar); } } console.log('foo: ', foo.bar, ' baz: ', baz.bar);
Variables are exposed using the export keyword, anything that is not exported is scoped to the module and inaccessible from the outside. Modules can span multiple files, adding a reference tag will let TypeScript know where to load the details about the existing module from.
/// <reference path="modules.ts"/> module baz { export class Bar { constructor(public foo: string) { } } } var bar:baz.Bar = new baz.Bar('fun!'); console.log('bar', baz.bar, 'class: ', bar);
External modules are the ones that compile to either AMD (require.js) or CommonJS (node.js). Within TypeScript you will use the ES6 module loading syntax. These are the types of modules that Angular 2 will use and so you will see this throughout the examples. Instead of using the module keyword, the file itself is considered the module. Variables and objects that are not exported are scoped to the file.
(modules_external.ts)
var foo:string = 'bar'; export function getFoo(): string { return foo; } export var BAR:string = 'BAR'; export class Baz { constructor(public name: string) {} }
This can then be referenced in another file like so (modules_external2.ts):
import {getFoo, Baz} from './modules_external'; console.log('foo: ', getFoo(), 'baz: ', new Baz('Herman'));
You can selectively import objects from the module, or use * to grab everything. If you are familiar with node.js or require.js then this will likely be familiar to you.
There is a lot more to TypeScript and ES6, and I encourage you to study the documentation and resources available for them, but we want to start working on Angular 2 applications now. This primer should enable you understand enough to proceed with that.
Creating a basic Angular2 App
Now that you have an understanding of TypeScript and the upcoming features of JavaScript, we can finally get to actually creating an Angular 2 application. First, we’ll need to download a few libraries. We need of course the main Angular 2 library, also systemjs so that we can load our module, and traceur which handles some of the polyfills for the ES6 features that Angular is using. I’ve actually had the most luck using a tool called jspm. Similar to npm jspm is designed to grab needed dependencies from both npm as well as github (and other repositories). It takes all of those dependencies and prepares them to be loaded using systemjs. It will generate a config.js file that will setup systemjs so that it’s easy to launch your apps. It will also build a bundle file for when you are ready to release.
npm install -g jspm
We’ll create a folder for our project and use jspm instead of npm to install our dependencies, it will augment the package.json that npm would normally use, you can use them side by side. If you are going to use the included source code files, then you can simply run npm install && jspm install and it should load everything you need.
jspm install angular2 traceur-runtime es6-shim reflect-metadata zone.js typescript
It will install all of these libraries and also install systemjs (we don’t have to tell it to). It will also create a config.js file that is populated with some of the information needed to pass to systemjs. Now we can create an index.html file that looks like this:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Getting Started with Angular 2</title> </head> <body> <app></app> <script src="jspm_packages/system.js"></script> <script src="config.js"></script> <script> System.import('app/hello'); </script> </body> </html>
There are a couple of things to note about what we have here, first we have the <app> element. Similar to Angular 1, we can create custom elements and bind them to the Angular context, we’ll define that in our code. We need to load system.js that jspm has installed for us, as well as the config file that it has generated. Finally, we’ll start everything up by importing the hello module from app, we’ll create that file in a moment. While jspm has generated a basic config file for us, we need to make a few additions to it in order to get everything running the way that we want. We need to add in options to pass to the TypeScript compiler, so that it knows that we are targeting ES5 browsers, using the systemjs module type, and also using the experimental decorators feature.
typescriptOptions: { "compilerOptions": { "target": "ES5", "module": "system", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false } }
We also want to have it automatically transpile our ts files into js files on the fly, to make development easier. In order to accomplish that, we need to define a package, which is essentially a folder, and tell it to look for ts instead of js files.
packages: { "app": { "defaultExtension": "ts" } }
You’ll also want to install the TypeScript definition file manager tsd so that the compiler and IDE can understand the Angular 2 library. TypeScript “typings” are like forward declarations of types and interfaces, as well as documentation. It lets TypeScript know the shape of 3rd party libraries that you might be using so that it can do its type checking against them.
npm install -g tsd
Once that is installed, we’ll use it to download the core set of definition files for Angular. It will create a “typings” folder that contains all of the various definition files. At the top of the folder, it will create a “tsd.d.ts” file which is a roll up of all the dependencies so that you can load just one file at the top of your app.
tsd install angular2 es6-promise rx rx-lite --save
Now we’ll create a folder called “app”, and inside a file called “hello.ts”. At the top we’ll need to include a reference to roll up typings file. We need to import the 3rd party libs and then we’ll import Component, View and bootstrap from the angular2 module.
/// <reference path="../typings/tsd.d.ts" /> import 'zone.js'; import 'reflect-metadata'; import 'es6-shim'; import {Component, View, bootstrap} from 'angular2/angular2';
A Component is similar to a directive in Angular 1. Any directive that requires a view, much like the items used in ng-view or a custom directive with a specialized view. Angular provides a @Component class decorator that is configurable. We pass in a selector that essentially translates directly to what you would pass into document.querySelector call. Since the selector we are passing in is just “app”, we are creating a element component using the tag <app> .
A component also requires a @View decorator that among other things allows us to describe the template that will be used by the component. We can use ES6 template strings to create nice multi-line templates, but we still use Angular’s double handlebars syntax to bind to “scope” variables.
Finally, the class that you are decorating is your controller. Similar to using the “controllerAs” syntax in Angular 1, the public variables and methods are available within the scope of the template.
@Component({ selector: 'app' }) @View({ template: ` <div>Hello {{ name }}</div> ` }) class App { name: string = 'World'; }
The last item that we imported is bootstrap, which much like in the first version is a method to initialize all of the components and modules within the framework. For now, we simply have one component, so we pass the App class to it to get things started. Instead of bootstrapping to a DOM element like we did in Angular 1, this time we are setting the root of our tree to a specific class (function). Much like the original bootstrap method, you can pass in an array of items for the dependency injector as well, we’ll do that later on.
bootstrap(App);
Because files are going to be dynamically loaded via ajax, you can’t simply open the index.html file in the browser. Instead you’ll need to host it as a local web server. You can use any web server that you want, if you don’t know of one to use, you can install live-server .
npm install -g live-server
Once that is installed, simply execute it from the location that you want to serve. It will open the browser for you and watch for changes to automatically refresh as you develop.
Components
Right now we have just a single Angular “application” root. We want to break things down into components to build up our application views. Let’s take our little “Hello World” message out of the main application, and create a separate component for it.
@Component({ selector: 'hello', properties: ['name'] }) @View({ template: '<div>Hello {{ name }}</div>' }) class HelloComponent { name: string = 'World'; }
As you can see, this looks very much like the App component that we used to have, and this is because they are both components. Everything in Angular 2 is just a tree of components. The other services and directives exist only to assist the components in doing their job, which is rendering a view.
One thing that is new here however, is the properties attribute added to the component definition. This allows us to bind values from attributes defined on the element to the properties of our component class.
Now we need to update our main App component to reference and display our HelloComponent.
@Component({ selector: 'app' }) @View({ template: ` <div> <hello [name]="myName"></hello> </div> `, directives: [HelloComponent] }) class App { myName: string = 'Ben'; }
Now instead of directly outputting the name into our template, we include the markup for our <hello> tag. This time around, attributes take a special syntax as well, for a non method (event) we use the square brackets. This helps differentiate the attributes from standard HTML attributes and decreases the work that everyone has to do to recognize them. Method attributes are surrounded in parentheses, we’ll take a closer look at them in a bit. Finally, we also need to declare the directives that are expected to be found in the template. This is because there is no Angular module system to list out the dependencies anymore, also it is easier on the compiler to not have to check every single element in the DOM.
Directives
Directives are similar to directives in Angular 1, however they do not have a view. In Angular 1 essentially Directives and Components were the same thing, now they are separate animals.
We can declare a directive using the @Directive decorator to decorate a class. Our selector should use the square brackets attribute notation. We want to pass in the color to hover via the same attribute as the directive, but since we have a hyphen in there, we need to rename it so that it can map to a property in the class. The host property allows us to configure some things on the “host” element. We want to hook into the mouseenter and mouseleave events, the syntax for events uses parentheses. On the right side we are mapping these events to method handlers in the class. The $event is available just like in Angular 1, however for this simple directive we don’t need it.
In order to access the element that the directive is attached to, we need a special type called ElementRef . We also need to be able to use dependency injection to get an instance of it passed to our class constructor. The @Inject decorator tells Angular to do just that. Once we have that, we can use it in our event handlers to change the background color of the element on “hover”.
@Directive({ selector: '[hover-color]', properties: ['color:hover-color'], host: { '(mouseenter)': 'on($event)', '(mouseleave)': 'off($event)' } }) class HoverColorDirective { color: string; el: ElementRef; private _oldColor: string; constructor(@Inject(ElementRef) el: ElementRef) { this.el = el; this._oldColor = this.el.nativeElement.style.backgroundColor || '#fff'; } on(event) { this.el.nativeElement.style.backgroundColor = this.color; } off(event) { this.el.nativeElement.style.backgroundColor = this._oldColor; } }
We also need to add the new types ( Directive ElementRef, Inject ) to our import statement. Then we can update our HelloComponent (or any component) to use our new directive.
import {Component, View, bootstrap, Directive, ElementRef, Inject} from 'angular2/angular2';
@Component({ selector: 'hello', properties: ['name'] }) @View({ template: '<div [hover-color]="color">Hello {{ name }}</div>', directives: [HoverColorDirective] }) class HelloComponent { name: string = 'World'; color: string = 'pink'; }
There are also built in directives, one of the more frequently used directives from Angular 1, ng-repeat, has returned, but is now called ng-for. We need to import the type again before we can add it to the component’s list of directive dependencies. We can either specifically import NgFor as I have done, or we can import a special array of directives called CORE_DIRECTIVES .
@Component({ selector: 'app' }) @View({ template: ` <h1><hello></hello></h1> <div> <hello *ng-for="#myName of names" [name]="myName"></hello> </div> `, directives: [HelloComponent, NgFor] }) class App { names: string[] = ['Ben', 'Pablo', 'Lukas', 'World', 'Angular']; }
The syntax for ng-for is a little different, you start it with an asterisk (*). This is special sugar to expand the directive into a template component. The hash sign on the syntax inside provides that variable name to the child scope inside. Since the documentation is not quite ready yet, this blog post explains this in greater detail.
Services
Obviously rendering static variables isn’t very exciting, we want to work with more complex data and services. Creating a service in Angular 2 means simply creating a class just like any other.
class DataService { getFriends(): string[] { return ['Ben', 'Pablo', 'Lukas', 'World', 'Angular']; } }
The DataService is a simple one with only one method, but now we can update our get the data about our friends from a service instead of baking it directly into the component.
While we’re at it, let’s further componetize our app, we can pull the friends listing out of the main App component and add it into a “friends-list”. When a component needs to access services, similar to how the view needs a list of directives, it needs a list of bindings. We pass in our DataService to the bindings, and then @Inject it into our constructor.
@Component({ selector: 'friends-list', bindings: [DataService] }) @View({ template: ` <div> <h2>Friends</h2> <hello *ng-for="#friend of friends" [name]="friend"></hello> </div> `, directives: [HelloComponent, NgFor] }) class FriendsList { friends: string[]; constructor( @Inject(DataService) service: DataService) { this.friends = service.getFriends(); } }
Finally, we can update our App component to use the new friends-list.
@Component({ selector: 'app' }) @View({ template: ` <friends-list></friends-list> `, directives: [FriendComponent, FriendsList] }) class App { }
Of course, even this service is really static, normally we are going to want to load data from an external source over HTTP. Angular has again provided a wonderful Http service that we can use.
Because the http module isn’t part of the angular2 module, but a separately loaded file, we need to add it to the paths in our config.js file so that jspm knows where to find it.
paths: { "npm:*": "jspm_packages/npm/*", "github:*": "jspm_packages/github/*", "angular2/http": "jspm_packages/npm/[email protected]/bundles/http.dev.js" },
We also need to install the typings file so that the IDE / compiler understands the the library.
tsd install angular2/http
A new import line at the top will pull in the types that we need. Specifically we need the Http service, and we also need HTTP_BINDINGS which is an array of other dependencies that we will pass into the bootstrap method.
import {HTTP_BINDINGS, Http} from 'angular2/http';
Now we need to add the Http service to the DataService class. Just like before we @Inject it into the contstructor, we’ll assign it to a member variable so that we can access it in later methods. Angular 2 favors Rx event streams, so we convert the request to a Rx stream and grab the json data.
class DataService { http: Http; constructor( @Inject(Http) http: Http) { this.http = http; } getFriends() { return this.http.get('friends.json') .toRx() .map(res => res.json()); } }
Here is the contents of our friends.json file, I’ve added another property to make it slightly more interesting than just an array of names.
[ { "firstName": "Pablo", "gender": "M" }, { "firstName": "Erica", "gender": "F" }, { "firstName": "Lukas", "gender": "M" } ]
Now our friends-list component can be broken down further and we’ll take the display of the friend out into another component. Our DataService is still being injected into the constructor, but now instead of just using the array directly, we need to call subscribe (which is another Rx method) to assign the results to our friends property.
@Component({ selector: 'friend', properties: ['person'] }) @View({ template: '<div>{{ person.firstName }} ({{ person.gender }})</div>' }) class FriendComponent { person = {firstName: 'Foo', gender: 'M'}; } @Component({ selector: 'friends-list', bindings: [DataService] }) @View({ template: ` <div> <h2>Friends</h2> <friend *ng-for="#friend of friends" [person]="friend"></friend> </div> `, directives: [FriendComponent, NgFor] }) class FriendsList { friends: Object[]; constructor( @Inject(DataService) service: DataService) { service.getFriends().subscribe(res => {this.friends = res; }); } }
Lastly we add the HTTP_BINDINGS to our bootstrap method, and then our service will pull the data from the remote source.
bootstrap(App, [HTTP_BINDINGS]) .catch(err => console.error(err));
Ready, Set, Go!
Working with alpha software is not for everyone. However, due to the popularity of Angular 1.x I think you can count on Angular 2 being important in the future. Even though the API is evolving and may be radically different by the time of the full release than right now, it’s good to get in at this early stage so that by the time that it does get released you’ll already be an expert. This is the best time to help mold the future of Angular as well, while it’s in an unrefined state. At the time of this writing the Angular Team is working hard towards a beta release. They are open to feedback and contributions, and pretty much the only way to come up with those is by using it.
I hope that you’ve enjoyed this tutorial and have some fun working with Angular 2. Please leave a comment below with any feedback or questions that you might have.