Note: This article is a bit out of date, but the Plunker online does work with the latest release candidate. Most of the scripts and imports are technically inaccurate now.
Angular2 is just around the corner. We’re on the 42nd alpha (and they’re coming 1-2x per week now), and the team is working on their boilerplate destroyers – anything they can simplify for mere mortals, they’re working on now.
Play with Angular2 in 3 seconds
All you have to do to play with Angular2 is head over to Plunker and open the editor. From there, Click on the New dropdown button (make sure to click the drop-down).
Select Angular2 -> TypeScript and you’ll get a fully functional basic Angular2 application shell, complete with the new RxJs-based Http service, fetching some basic data from a JSON file. This gives you a great way to start.
Now, go ahead and save it, mess around. Fork it to another project if you want to. And don’t forget to sign up with GitHub, and you can save your Plunks and share them with others. You can even download a zip of your plunk at any time so you can debug it locally.
Let’s look at the sample application
I’m going to give you a quick walkthrough of the Angular2 application on Plunker (because it’s quicker than building another strawman), just to explain what the code is doing.
Pre-angular setup – the transpiler and module loaders
First of all, remember this project is using TypeScript, a type-safe language based on JavaScript (ES6) that is being dynamically transpiled to your browser’s ES5 interpreter. The way that happens is by mounting these four scripts:
<script src="https://code.angularjs.org/tools/traceur-runtime.js"></script> <script src="https://code.angularjs.org/tools/system.js"></script> <script src="https://code.angularjs.org/tools/typescript.js"></script> <script src="config.js"></script>
The first, traceur-runtime
, is just a series of shims for supporting various ES6 features in your current browser. For example, typical ES5 browers don’t yet have parseInt
as a function on Number
, just on window
. But ES6 makes that Number
object more complete, so the traceur-runtime
adds it, along with a number of other features (like array.prototype.forEach
).
Next, we have system.js
, which is the loader for modules provided by the SystemJS
project on GitHub. It’s based on the ES6 module loader polyfill. We need that because Angular2 is a module-based system (hooray!), which means that your code is now loaded by dynamically loading JS as needed. That helps keep your memory footprint low.
Next, we have typescript.js
, the dynamic TypeScript transpiler. In production, we wouldn’t load typescript on the fly, but here we are using it for the toy application, and that’s OK. I will be blogging about a compiler-based approach in an upcoming article, but I wanted you to be able to play with Angular2 first.
The last one is the system.js
configuration file, config.js
. This file allows you to hook the dynamic transpiler for TypeScript into the loader. Here are the contents of this config file, annotated with the sample developer’s comments:
System.config({ //use typescript for compilation transpiler: 'typescript', //typescript compiler options typescriptOptions: { emitDecoratorMetadata: true }, //map tells the System loader where to look for things map: { app: "./src" }, //packages defines our app package packages: { app: { main: './main.ts', defaultExtension: 'ts' } } });
As you can see, the app is letting system.js
process all JavaScript files ending in the extension .ts
through the TypeScript transpiler. This happens on the fly – view your browser’s network connections as it fetches the page and you’ll see the loader load the ts. Then the TypeScript
transpiler processes it and provides the JS to the module.
What’s in a module?
In a word, components. Everything in Angular is a component, which at its base is simply a JavaScript object, decorated with annotations to describe the component itself and to describe any view it needs. The object contains the state of the component – goodbye to $scope
! We define the object in TypeScript
using the ES6 class
keyword – yes, we have classes in ES6 – which reduces down to a constructor function + properties in ES5.
Modules are a collection of components, which we define in a script file. Some components are tied to views directly (and generally use the @View
annotation). To show you this, here’s the beginning of the app.ts
component, which imports several components from their modules:
import {Component, View, CORE_DIRECTIVES} from 'angular2/angular2'; import {PeopleService} from './peopleService'; import {Person} from './person';
(I added the semicolons, force of habit)
In Angular2, we don’t just import the entire script for the framework. If we’re using ES6 or TypeScript, we can import the things we need, and ignore setting up the ones we don’t. In this case, they are loading Component
and View
, two ES6 classes we actually use as annotations below, and we import some of the key directives (tags/attributes) from the framework. Then, we import PeopleService
, our service object (the one providing our data) and the Person
class definition, so that we can process the results.
This import
syntax is the standard for ES6
, and it allows for destructuring (pulling multiple objects out of a core module) so you can use syntax like the first one:
import {Component, View, CORE_DIRECTIVES} from 'angular2/angular2'
and be assured that you will get three imported items from the module defined at the path of angular2/angular2
.
Back to the HTML – the rest of the app shell
Ok, I digressed a little bit… But now we come to how Angular itself is loaded. There are numerous options here
- a fully-encapsulated file,
angular2-sfx.js
, which pre-loads everything for ES5 browsers, - a system.js-based module-based collection of files –
angular2.js
,http.js
,router.js
, etc… this is what the sample is using - individual typescript or ES6 files for each component, for use in kitting up the code with a transpiler (see the next blog post)
- Dart-based libraries – I know zip about Dart, but you can use the dart-transpiled versions too
So, the app is going to use the ES5 system.js
version of Angular, and we need the http
library too. The sample builder included both:
<script src="https://code.angularjs.org/2.0.0-alpha.42/angular2.min.js"></script> <script src="https://code.angularjs.org/2.0.0-alpha.42/http.min.js"></script>
Now, they are able to bootstrap the module loader and load our first component. As a little preview of what is going on in the module itself, it’s a component they registered with the tag <my-app>
, which is why the <body>
tag includes that tag. Here is the bootstrapping code on the page after our prior scripts:
<script> System.import('app') .catch(console.error.bind(console)); </script> </head> <body> <my-app> loading... </my-app> </body>
And that’s all we have to do to get Angular2 to boot on a page. We use System.import('app')
, which requests app.ts
, which is put through the system.js
loader, transpiled into ES5, and executed. We chain a handler onto any error in the module loader, binding the error messages to the console (for some reason I believe this is necessary but I will have to research it another day).
On to our first component – App.ts
Let’s take a look at our first component. There’s a lot here, so it is a good one to start with. Again, it starts with the import statements:
import {Component, View, CORE_DIRECTIVES} from 'angular2/angular2' import {PeopleService} from './peopleService' import {Person} from './person'
Next, it uses Component
and View
as annotations on the ES6 class at the bottom of the file. If you’re a Java programmer, you might feel strangely at home. This is TypeScript
syntax, and it provides the component object definition as well as how it renders the view. First, let’s look at the @Component
block:
@Component({ selector: 'my-app', bindings: [PeopleService] })
Let’s break this down – the selector
property states that our component is located on the page as the tag <my-app>
. Angular is also informed that the PeopleService
ES6 class will be injected into this object as a usable property.
The view
Next, the @View
annotation – how does this component render? Let’s see:
@View({ template: ` <div> <h2>Hello Angular2!</h2> <my-person *ng-for="#person of people" [name]="person.name" (hello)="saidHello($event)"> </my-person> </div> `, directives: [CORE_DIRECTIVES, Person] })
Before you spit out your coffee, I’ll say I’ve really come to like this syntax. First off, the ability to embed small templates with line breaks in the code using the template
property is huge. You can see the content of the view while editing the business logic – no more side-by-side editors. We are using the back-tick key to start and end the string, which is ES6’s multiline string feature. Some editors like Atom and WebStorm can even do syntax highlighting in these strings. React eat your heart out with your JSX, we have it too.
The new Template Syntax
Let’s dig into the template. First, we render a div, which contains a title (no biggie) and then, wait, another component! Yep, it’s components all the way down. You’ll rarely need to build your own directives now, because what you used to have to write a directive for now becomes your standard component. Also, where you used to build a controller, then render a view, becomes a component that contains a view. The controller? Not gone, but it’s the class definition.
Now, let’s dig into the use of the my-person
component they’re mounting here. It is using Angular2’s new NgForOf
directive, which is included in the CORE_DIRECTIVES
array imported at the top of the class. The <my-person>
component is being shown for each object inside of an array called people
, which you’ll see we are loading into our component below at startup.
There are several variants of syntax for the NgForOf
component. This is the shortest one:
*ng-for="#person of people"
It is short for
ng-for="var person of people"
From that point on in the tag, person
is the iterated value.
There are two bindings here in the template – a property binding, which sends an object into the component, and an event binding, which allows the component to emit an event and call a method in our component. First, the property binding:
[name]="person.name"
This means set the property name
to the value evaluated as person.name
in our template. Without the []
wrapper, we’d have to do something like:
name="{{ person.name }}"
The other binding is an event:
(hello)="saidHello($event)"
Because Angular2 now can tell whether an expression is a property binding [propName]
or an event (eventName)
, there is less parsing to do and the intentions are obvious to the developer using the template.
The ES6 / TypeScript class definition for the component
Now comes the implementation guts. The file, app.ts
, is a module (every ES6 file can be a module), and it exports a class, App
:
export class App {
constructor(public peopleService:PeopleService) {
peopleService.people
.subscribe(people => this.people = people);
}
saidHello($event){
alert(You said hello to ${$event}
)
}
}
So we see they’ve exported App
as a class definition, which we picked up and used in the HTML of index.html
to boot the app. They provide an ES6 (well, TypeScript) constructor here. And the constructor injects the PeopleService
we referred to in @Component({ ... bindings: [PeopleService]})
, calling it peopleService
in the component. During the construction of this component, Angular creates and injects the peopleService
object, and then it is used to fetch people using the Angular2 reactive Http
object. This two liner:
peopleService.people .subscribe(people => this.people = people);
shows how we have switched from promises
in Angular2 to the RxJs
library from Microsoft to do async calls. It is more powerful, and treats the result like a stream of data. I will have more detailed blog posts about this later, but what happens is that as the peopleService sends a result packet, it triggers our little arrow function in subscribe (people => this.people = people
), which places the results in the component as people
. And there you have it, state in the component, fed by a service external to the component.
The PeopleService service
Finally in this post we’ll look at their PeopleService
service object. It’s remarkably simple – just an ES56 class decorated with @Injectable()
to make it available to our binding and constructor function:
//a simple service import {Injectable} from 'angular2/angular2'; import {Http} from 'angular2/http'; @Injectable() export class PeopleService { constructor(http:Http) { this.people = http.get('api/people.json') .map(res => res.json()); } }
There are only two things at work here – the class is injectable so it can be inserted into another Angular component, and we are using the http.get
API, which is now returning an Observable
object as a result. We use functional programming to send the result through a series of functions, like map
, flatMap
, filter
, reduce
, flatMapLatest
(this one cancels the prior in-flight requests if a new one appears, something promises cannot do), and others.
In the map(res => res.json())
call we are taking the data and converting from Json into a JavaScript object (which we didn’t have to do in Angular 1, but which provides flexability in Angular2), and returning it to the subscriber (our App
instance) as an object result. Again, more about these functions and the Microsoft RxJs library later.
Going forward, have a ball!
Hopefully armed with this high-level walkthrough of the sample, you can start hacking. Take a look at the Person object, add some fields, maybe play with the functions in this service. Can you count the elements that come back? Maybe make new Json files and experiment with summarizing, searching, etc?
Remember to just go in to Plunker and use New... Angular2... Typescript
to quickly start with this as a base. Or, if you want to pare it down, create your own base template and use it for your projects instead. There are also purely ES5 and even ES6 versions of this sample in the same menu, so you can see how you might approach this if you were programming in those platforms instead.
In our next blog post we’ll dive into an example seed project I built with Angular2, TypeScript, Gulp, and other tools. That one transpiles as a compile step, and uses live reload to refresh a page once it is finished.