These days, front-end JavaScript frameworks are a trending topic across the developer community. Heavy usage of these frameworks has made the JavaScript language designers think of making the language better, and fit it to the scale at which it is being used these days. The ECMAScript 6 specification has brought in a significant number of changes to the language and the language designers have decided to make these updates more frequent to keep the language relevant to the changing landscape of web development. Browser vendors are also keeping up by progressively implementing ECMAScript 6 changes over time. [Recommended reading - https://www.dotnetcurry.com/javascript/1090/ecmascript6-es6-new-features ]
These changes to the language and the web as a whole, makes the ecosystem better. The frameworks built on this new ecosystem will be able to provide better ways of solving problems that the developers are actually facing. This is one of the reasons why the Angular team has decided to write the next version, Angular 2 from scratch leveraging the latest enhancements of the web.
Though Angular 1.x has proved to be a good framework for building rich internet applications (RIA), it was built with older browsers in mind. The framework had to build its own module system and a set of abstractions to make its usage easier. By leveraging the new features of the web, the framework won’t have to define certain abstractions, and will instead focus on making the experience of working on it better in many other ways. Angular 2 still adopts some of the good things from Angular 1, but at the same time, it abandons features that complicate things while writing large applications or, the features that are already covered by the language. In case you are wondering about a migration strategy from Angular 1 to Angular 2, please refer to this link - http://angularjs.blogspot.in/2015/08/angular-1-and-angular-2-coexistence.html
In case you haven't heard of it earlier, Microsoft and Google are working together to help make Angular 2 better. Angular has been using AtScript, a superset of Microsoft’s TypeScript and both these languages have been merged now into TypeScript. Going forward, you will be writing Angular 2 applications using TypeScript.
In this article, we will take a look at the core concepts and the basic features of Angular 2.
This article is published from the DNC Magazine for .NET Developers and Architects. Download this magazine from here [Zip PDF] or Subscribe to this magazine for FREE and download all previous and current editions
Disclaimer: Angular 2 is still in alpha. The code snippets shown in this article and in the supporting sample code may not work in the future releases.
Angular 2 Core Concepts
Like Angular 1, Angular 2 (currently in alpha) is built on a set of concepts that are used throughout the framework and they would be used directly or, indirectly while writing applications. Let’s take a look at them.
Change Detection
At the heart of any front-end web framewor is the technique used for change detection. Angular 2 adds a powerful and much flexible technique to detect changes on the objects used in the application. In Angular 1, the only way the framework detects changes, is through dirty checking. Whenever digest cycle runs in Angular 1, the framework checks for changes on all objects bound to the view and it applies the changes wherever they are needed. The same technique is used for any kind of objects. So here we don’t have a chance to leverage the powers available in objects - like observables and immutables. Angular 2 opens this channel by providing a change detection system that understands the type of object being used.
In addition, the change detectors in Angular 2 follow a tree structure to detect changes. This makes the system predictable and it reduces the time taken to detect changes.
If plain JavaScript objects are used to bind data on the views, Angular has to go through each node and check for changes on the nodes, on each browser event. Though it sounds similar to the technique in Angular 1, the checks happen very fast as the system has to parse a tree in a known order.
If we use Observables or, Immutable objects instead of the plain mutable objects, the framework understands them and provides better change detection. Let’s delve further into the details:
· Immutable Objects: As the name itself says, an immutable object cannot be modified as it is created. A change made in the object re-creates the object itself. The re-creation kicks in an event to notify user about the change. So the bindings under the subtree using immutable objects, are not parsed until such an event occurs. When the event is raised, the subtree is checked and the changes are applied on it. The checks will not happen on browser events following this check, unless the event is raised again.
· Observable Objects: The observable objects implement a reactive mechanism of notifying the changes. They emit events when the object changes. Subscribers to these events get notified whenever the object changes and they can use the new value to update UI or, perform the next action upon receiving the notification.
Angular 2 understands observable objects. When an object sends a change notification, the framework checks for changes in the subtree under the node containing the component depending on the changed object. This subtree won’t be checked for any changes until another change event on the same object is raised.
Both immutable objects and observable objects reduce the number of comparisons to be made while detecting changes. This improves the performance of the application.
There is no restriction on usage of libraries for the two types of objects discussed above. We can use any library according to our convenience. It is also possible to use both immutable objects and observable objects together in the same application.
We need to tell the framework that the component uses one of these objects. It can be done using a property in the component annotation.
@Component({
selector:'my-component',
changeDetection: ON_PUSH
})
Dependency Injection
Dependency injection (DI) has been a part of typed object-oriented languages for quite some time and the pattern continues to play an important role there. It makes the code testable and maintainable as usage of the pattern reduces dependency on concrete objects in components of the application. Many JavaScript developers, including me, felt happy when we saw DI implemented in the Angular framework. Without any doubt, it is one of the reasons behind wide usage of the framework.
Angular 2 continues to use this pattern. It now provides us with a simple and yet powerful DI container. The DI container in Angular 2 has a single API for injecting dependencies and it also provides ways to control lifecycle of dependencies and specify the type of dependency to be injected when a type is requested. The DI works in the same hierarchy in which the components are built. Dependencies have to be declared along with annotations of the components. A dependency declared on a component can be used either by the same component or by any of the children of the component. Children components can override the way a dependency is declared in the parent as well.
Zones
After the application starts, we need a process to keep running behind the scenes that kicks in the process of change detection so that the model and the UI are always in sync. Angular 2 includes a library called Zone.js, which helps us in keeping things always in sync. A zone is an execution context that persists across async tasks. All operations performed in Angular 2 are carried under this context. We don’t need to invoke them or attach operations to them. Zones know what kind of operations to be handled in the context. Because of zones, we don’t have to take any pain while including non-angular UI components in Angular 2.
Directives
Like in Angular 1.x, directives in Angular 2 are used to extend HTML. The directives in Angular 2 are more similar to the way DOM appears and they provide ways to get into life cycle events to take better control over the way a directive works. Architecture of the directives in Angular 2 embraces bindings a lot and hence reduces the need of direct DOM manipulation. Also, we don’t need to deal with different restrict types as well as with the camel case notation while naming directives; both of these properties are handled by a single selector property.
There are three different types of directives in Angular 2:
· Component Directives: Everything in an Angular 2 application is a component. The application starts by bootstrapping a component and other components are rendered as children of this component. So hypothetically if we take an aerial view, an Angular 2 application looks like a tree of components. The components are used to define a new HTML element. This feature is built using the techniques introduced in HTML5 web components. If the browser doesn’t support any of the features, the framework emulates them.
Code of a component includes a class and two annotations. The following snippet shows structure of a component written in TypeScript (if you are not familiar with TypeScript, you may want to check a basic post on the topic: Hello TypeScript – Getting Started):
@Component({
selector: 'my-component', //Name of the component, it will be used in HTML
bindings: [MyService] //List of services to be used in the component or, its children
})
@View({
template: '<div>{{sampleText}} </div>', //Template of the component
directives: [] //List of directives to be used in the component
})
class MyComponentClass{
public sampleText: string;
constructor(private svc: MyService){
this.sampleText = this.svc.message;
}
}
As we can see, the component is a class decorated with the annotations Component and View. An object of this class is the view model for the component. Any public property defined in this class can be used to bind data in the component’s template.
· Decorator Directives: A decorator is the simplest kind of directive. It is used to extend behavior of an HTML element or an existing component. This directive doesn’t make a lot of impact on the view, but helps in achieving certain small yet important functionalities on the page. A decorator directive is a class with the Directive applied on it. The following snippet shows an example of a decorator directive:
@Directive({
selector:'[my-decorator]',
host: {
'(mouseover)': 'myFunction()'
}
})
class MyDecoratorClass{
//class definition
}
If you observe this snippet, the directive interacts with the host element through configuration. And if we use this feature effectively, we can avoid performing direct DOM manipulations in the component. Such behavior makes the code easy to port to non-browser based applications.
· Directives with ViewContainer: The directives that use a ViewContainerRef object can take control over the content of the element on which they are applied. These directives can be used when template of the view has to be shown/hidden based on a condition, to iterate over or, to tweak the content based on a scenario. The directives like ng-for and ng-if fall into this category.
Bindings and Templates
Like every front-end framework, Angular 2 has a very good support for binding. As already mentioned, view-model for any component is an object of its class. Public properties and methods of the classes can be used to show data, handle events and handle properties of the HTML elements. The framework introduces a different syntax for data binding.
Public properties of the component class can be bound to any element on the view. For example, following snippet binds the property heading to an h1 element:
<h1>{{heading}}</h1>
The public properties can be assigned as value to any of the properties as well. The properties can be bound in two ways:
<span [title]=”myTitle”> Some text in the span </span>
Or,
<span bind-title=”myTitle”> Some text in the span </span>
The bind-title attribute in the above snippet is not a decorator directive, the prefix bind is a convention and it is an alternative syntax to the square bracket [attribname] syntax used earlier. The bind- prefix can be used with any valid HTML attribute to bind the property with a value on the view model.
Similarly, public methods defined in the component class can be used to handle events.
<button (click)=”clicked()”>Click Here!</button>
Or,
<button on-click=”clicked()”>Click Here!</button>
Angular 2 doesn’t have direct support for two-way binding. All property binders are one-way. It has a work around to support two-way binding. Following is the syntax:
<input type=”text” [(ng-model)]=”name” />
The two-way binding in Angular 2 is a combination of property binding and event binding. Value of the property is bound to text of the textbox and any change in the text raises an event to update the model. In fact, the above snippet is the shorter form of the following snippet:
<input type=”text” [ng-model]=”name” (ng-model)=”name=$event” />
Services
Angular 2 doesn’t have the concept of services. Anything other than directives are simple classes. Good thing is, we can apply dependency injection on these classes and these classes can be injected into any directives. We can consider any plain simple class as a service. The following snippet shows a service and its usage in a component using TypeScript:
class Message{
greeting: string;
constructor(){
this.greeting = "A sample message";
}
}
@Component({
selector: 'show-message',
bindings: [Message]
})
@View({
template: '<div>{{message}}</div>'
})
class ShowMessage{
message: string;
constructor(messageSvc: Message){
this.message = messageSvc.greeting;
}
}
Let’s build a component
Now that we had a tour around some new features of Angular 2, let’s build a simple component. An Angular 2 application can be written using ECMAScript 5, ECMAScript 6 (ECMAScript 2015) or, TypeScript. The code written in ES6 and TypeScript is almost similar except that TypeScript would have support for types. We will build a simple music player component using ES5 and then using TypeScript.
Note: Instead of writing the component in both TypeScript and ES5, I could have used a TypeScript compiler to generate ES5 code. However my intention in this article is to show how Angular 2 code can be written using both TypeScript and ES5.
Music player using EcmaScript 5 (ES5)
The Angular 2 framework has two different versions of its JavaScript file, one to be used with ES6 or, TypeScript and the second for ES5. If you visit Angular’s code library for current version (alpha 37), you will find multiple versions available for each file. The Angular 2 core framework’s ES5 version is angular2.sfx.dev.js and ES6 version is angular2.dev.js. For the ES5 example, we will use angular2.sfx.dev.js.
After including this file on the page, we can start writing some JavaScript. At first, let’s create a service to store data. It can be any constructor function with some public fields. In the component, we will be showing the current song being played and next and previous buttons with tooltips showing the corresponding songs. We just need three fields assigned in the constructor function.
function MusicPlayerInfo() {
this.nowPlaying = "Yaad kiya dil ne";
this.next = "Hosh walo ko khabar";
this.prev = "Hotho se chulo tum";
}
To show the next and previous songs, we need to create a directive that uses data on the above service and displays name of the song in a tooltip. The Angular 2 library exposes an API that makes the code of directive look very close to the ES6 syntax. As already discussed, we need to define a class and apply the directive decorator to make it a directive. ES5 version defines the methods Directive and Class to make this task easier. The following snippet shows code of the directive:
var SongTip = ng.Directive({
selector: '[song-tip]',
properties: [
'label: song-tip'
],
host: {
'(mouseover)': 'show()',
'[title]': 'title'
}
})
.Class({
constructor: [MusicPlayerInfo, function (info) {
this.info = info;
}],
show: function () {
this.title = this.info[this.label];
}
});
In the above snippet:
- The properties option in the directive decorator is used to add properties to instance of the directive class with value assigned to a field on the element. Here, a field named label is added to the class that would hold value assigned to song-tip
- The host option is used to interact with the host element of the directive. Here we are handling the mouseover event and setting title to the element using a method and a property on the directive object
- Constructor defined in the class depends on the service MusicPlayerInfo. So the dependency is specified in the annotation. We haven’t bound this dependency yet, we will do that in the next component
We need to have a component element to show play, current and previous buttons. We will call this component MusicPlayer. The previous and next buttons will have the SongTip directive applied. It would use the field nowPlaying from the MusicPlayerInfo service to show the currently playing track. The following snippet shows code of the component:
var MusicPlayer = ng.Component({
selector: 'music-player',
bindings: [MusicPlayerInfo]
})
.View({
templateUrl: 'templates/musicPlayer.html',
directives: [SongTip]
})
.Class({
constructor: [MusicPlayerInfo, function (musicInfo) {
this.nowPlaying = musicInfo.nowPlaying;
}]
});
Following is the mark up in the musicPlayer.html file:
<div class="col-sm-6">
<div class="col-sm-3 text-center"><span class="glyphicon glyphicon-step-backward" song-tip="prev"></span></div>
<div class="col-sm-6 text-center"><span class="glyphicon glyphicon-play"></span><br/>{{nowPlaying}}</div>
<div class="col-sm-3 text-center"><span class="glyphicon glyphicon-step-forward" song-tip="next"></span></div>
</div>
<div class="col-sm-6"></div>
A few things to observe in the above snippet are:
- The bindings option in component decoration says the component depends on MusicPlayerInfo. That means it can be injected in the component and any of its children components or, directives
- The directives option on the view says the component will use the directive SongTip
- The field nowPlaying created in the constructor is used in template of the component
Let’s create another component MusicPlayerHost to host the above component and this component will be used for bootstrapping the application. The following is the snippet showing the code of the component:
var MusicPlayerHost = ng.
Component({
selector: 'music-player-host'
})
.View({
template: '<h2>{{message}}</h2><music-player></music-player>',
directives: [MusicPlayer]
})
.Class({
constructor: function () {
this.message = "My music player";
}
});
As the Angular 2 application has to start with a component, let’s bootstrap the application using the MusicPlayerHost component. The bootstrap process has to begin after the DOM content is loaded, so we need to add a listener to the DOMContentLoaded event to bootstrap the application.
document.addEventListener('DOMContentLoaded', function () {
ng.bootstrap(MusicPlayerHost);
});
Music Player using TypeScript
Setting up the environment with TypeScript needs some efforts. The TypeScript code has to be converted to JavaScript before the application is loaded in the browser. The process of converting TypeScript to JavaScript is called transpilation. The word transpilation is derived from the words translation and compilation. In addition, we should have a module loader to load the transpiled modules along with the modules exposed by Angular 2. The demo code contains the following NPM packages to fill these gaps:
- typescript, gulp and gulp-typescript to transpile TypeScript
- systemjs to load modules
- traceur to make the browser understand Angular 2’s annotations
Note: We are using Gulp to perform the task of transpilation. If you are not familiar with Gulp, you may read the section on Gulp in the article Using Grunt, Gulp and Bower in Visual Studio 2013 and 2015
The typescript task defined in gulpfile.js transpiles the files and generates sourcemaps. This is shown below:
var gulp = require('gulp'),
gts = require('gulp-typescript'),
ts = require('typescript'),
sourcemaps = require('gulp-sourcemaps');
gulp.task('typescript', function(){
var tsProject = gts.createProject('tsconfig.json', {
typescript: ts
});
return gulp.src(['typings/**/**.ts', 'typescript/*.ts'])
.pipe(sourcemaps.init())
.pipe(gts(tsProject))
.pipe(sourcemaps.write('../maps', { includeContent: false, sourceRoot: '/typescript' })
.pipe(gulp.dest('js'));
});
This task copies all of the transpiled files into the js folder. The HTML file rendering the application loads the helper libraries and then loads the main module of the application using SystemJS, which in turn loads all other modules.
<script src="\node_modules\traceur\bin\traceur-runtime.js"></script>
<script src="\node_modules\systemjs\dist\system.src.js"></script>
<script>
System.config({
defaultJSExtensions: true
});
</script>
<script src="\node_modules\angular2\bundles\angular2.dev.js"></script>
<script>
System.import('/js/bootstrap');
</script>
All of the code that we wrote in the previous section will take a different form with TypeScript classes. The following is the class for MusicPlayerInfo service:
export class MusicPlayerInfo{
nowPlaying: string;
next: string;
prev: string;
constructor(){
this.nowPlaying = "Yaad kiya dil ne";
this.next = "Hosh walo ko khabar";
this.prev = "Hotho se chulo tum";
}
}
The SongTip directive needs the Directive annotation and the service MusicPlayerInfo to be imported into the module and then use them in the code. Annotations in Angular 2 are decorators (a feature to be released in ES7, and is already implemented by TypeScript) with metadata information. As TypeScript understands decorators but not annotations, as they are not a part of the standard language, we need to use the traceur compiler (which is already included) to parse them on the browser. Following snippet shows the code of this directive:
import {Directive} from 'angular2/angular2';
import {MusicPlayerInfo} from './MusicPlayerInfo';
@Directive({
selector:'[song-tip]',
properties: [
'label: song-tip'
],
host: {
'(mouseover)': 'show()',
'[title]':'title'
}
})
export class SongTip{
title: string;
label: string;
constructor(private musicPlayerInfo: MusicPlayerInfo){}
show(){
this.title = this.musicPlayerInfo[this.label];
}
}
Notice the constructor in the above class. It accepts a parameter of type MusicPlayerInfo and we don’t need to add anything in addition for dependency injection, as the type specification in the constructor itself is used for injecting the dependency.
Similarly, the MusicPlayer component needs the annotations Component and View from Angular’s core modules and MusicPlayerInfo from the current folder. The annotations Component and View have the same responsibilities as they had in previous section; they just appear a bit different.
import {Component, Directive, View} from 'angular2/angular2';
import {MusicPlayerInfo} from './MusicPlayerInfo';
import {SongTip} from './SongTip';
@Component({
selector:'music-player'
})
@View({
templateUrl:'templates/musicPlayer.html',
directives:[SongTip]
})
export class MusicPlayer{
nowPlaying: string;
constructor(info: MusicPlayerInfo){
this.nowPlaying = info.nowPlaying;
}
}
The MusicPlayerHost component is straightforward. It uses the above component, so it has to be specified in directives
import {bootstrap, Component, View} from 'angular2/angular2';
import {MusicPlayerInfo} from './MusicPlayerInfo';
import {SongTip} from './SongTip';
import {MusicPlayer} from './MusicPlayer';
@Component({
selector:'music-player-host',
bindings: [MusicPlayerInfo]
})
@View({
template: '<h2>{{message}}</h2><music-player></music-player>',
directives:[MusicPlayer]
})
class MusicPlayerHost{
message: string;
constructor(){
this.message = "My music player";
}
}
To bootstrap the application, the above component can be passed to the bootstrap function.
bootstrap(MusicPlayerHost);
Conclusion
Angular 2 is written from ground-up using latest features available in the web ecosystem and it brings several significant improvements over the framework’s older version. While it retires a number of Angular 1 features, it also adopts a number of core concepts and principles from older version of the framework. After collaborating with the TypeScript team at Microsoft, both the teams are working really hard to create a great framework and they are also working with TC39 team to make JavaScript a better language. Let’s try it, explore and provide feedback to the core team on making it better!
This article was reviewed by Gil Fink.
Download the entire source code from GitHub at bit.ly/dncm21-angular2features
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Rabi Kiran (a.k.a. Ravi Kiran) is a developer working on Microsoft Technologies at Hyderabad. These days, he is spending his time on JavaScript frameworks like AngularJS, latest updates to JavaScript in ES6 and ES7, Web Components, Node.js and also on several Microsoft technologies including ASP.NET 5, SignalR and C#. He is an active
blogger, an author at
SitePoint and at
DotNetCurry. He is rewarded with Microsoft MVP (Visual Studio and Dev Tools) and DZone MVB awards for his contribution to the community