Angular is undoubtedly one of the most popular web application frameworks out there today. It powers countless web apps in the wild, and has cemented its fame as the front-end part of the extremely popular MEAN stack.
A new milestone was added to the history of the framework when the beta of Angular 2 was released in December 2015. It made a dramatic shift towards component-based architecture, rearranged some internal concepts, shunned all backward compatibility with Angular 1.x, and pushed users towards using Microsoft’s Typescript, a superset of the JavaScript language.
All of this created quite a stir in the community.
In this post, we are going to explore the differences between the classic Angular 1.X and the more recent Angular 2. Let’s start by introducing this shiny new version of the framework (currently RC6).
What is Angular 2?
Angular 2 is built around the concept of components, and more precisely, with the Web Components standard in mind. It was rewritten from scratch by the Angular team using Typescript (although you can use it with ES5, ES6, or Dart as well). The digest cycle from Angular 1.X has been replaced by another internal mechanism known as “Change Detection”. This feature, along with other improvements and tweaks, yields a considerable increase in performance (up to 5 times faster, according to some official sources).
A Note On Typescript…
We’ll use Typescript for demonstrating the new aspects of Angular 2, mostly because that’s the way the official documentation is written, and you’ll get the most benefits getting to know Angular 2 this way. It’s also safe to assume that most of the tutorials and articles on Angular 2 will also feature Typescript.
Barebones App Comparison
For simplicity’s sake, let’s take Plunkr’s templates for Angular 1.5 and Angular 2 as a foundation, and weed out unnecessary code and files like README. It’ll feature a simple component/directive called “coffee-machine” that’ll insert the string “Have a nice cup of coffee” in the HTML view.
We will analyze the two projects in the next section, but for now, here are the two projects.
Angular 1.X
With this version of Angular, we instantiate the app and declare a directive and a nominal controller in order to keep things simple. Here is the link to the Plunker project.
Here is the index.html
file:
<!-- index.html -->
<!DOCTYPE html>
<html ng-app="coffeeApp">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js@1.5.x" src="https://code.angularjs.org/1.5.8/angular.js" data-semver="1.5.8"></script>
<script src="app.js"></script>
</head>
<body ng-controller="coffeeController">
<coffee-machine></coffee-machine>
</body>
</html>
And a simple app.js
file containing our coffeeMachine
directive:
// app.js
angular.module('coffeeApp', [])
.directive('coffeeMachine', function() {
return {
restrict: 'E',
scope: {},
template: '<p>Have a nice cup of {{serveCoffee()}}</p>',
link: function(scope, element, attributes) {
scope.coffee = 'coffee';
scope.serveCoffee = function() {
return scope.coffee;
};
}
};
})
.controller('coffeeController', function($scope) {
$scope.coffee = 'coffee';
$scope.dispenseCoffee = function(coffee) {
return "Have a nice cup of " + coffee;
};
}
);
Angular 2
With Angular 2, we load everything we need before defining our component. This project also comes with a config.js
file not included below that loads the needed libraries and define Typescript as the transpiler. Here is the link to the Plunker project, containing the config.js file.
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<base href="." />
<title>angular2 playground</title>
<link rel="stylesheet" href="style.css" />
<script src="https://unpkg.com/zone.js@0.6.21/dist/zone.js"></script>
<script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
<script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
<script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>
<script src="config.js"></script>
<script>
System.import('app')
.catch(console.error.bind(console));
</script>
</head>
<body>
<coffee-machine></coffee-machine>
</body>
</html>
// app.ts
//our root app component
import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
@Component({
selector: 'coffee-machine',
template: `<p>Have a nice cup of {{name}}</p>`
})
export class App {
constructor() {
this.name = 'coffee'
}
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class CoffeeModule {}
// main.ts
// main entry point
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {CoffeeModule} from './app';
platformBrowserDynamic().bootstrapModule(CoffeeModule)
Bootstrapping
Let’s start our comparison by looking at how our applications are set up.
Angular 1.X
We declare our Angular 1.X application via the ng-app
directive in our HTML template:
<html ng-app="coffeeApp">
Angular 2
For Angular 2, the application is initiated in our code by registering CoffeeModule
via the bootstrapModule()
function, making it the “parent” module:
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {CoffeeModule} from './app';
platformBrowserDynamic().bootstrapModule(CoffeeModule)
Where are Directives, Controllers and $scope?
Roughly speaking, a component in Angular 2 replaces what was known as a directive and becomes the foundation of any Angular 2 application. Controllers and $scope
are gone. Sorry for your loss, but it was changed with a good reason in mind - the rise of the Web Components standard.
Angular 1.X
In Angular 1.X, we have a very simple controller and a directive:
// app.js
angular.module('coffeeApp', [])
.directive('coffeeMachine', function() {
return {
restrict: 'E',
scope: {
},
template: '<p>Have a nice cup of {{serveCoffee()}}</p>',
link: function(scope, element, attributes) {
scope.coffee = 'coffee';
scope.serveCoffee = function() {
return scope.coffee;
};
}
};
})
.controller('coffeeController', function($scope) {
$scope.coffee = 'coffee';
$scope.dispenseCoffee = function(coffee) {
return "Have a nice cup of " + coffee;
};
});
Angular 2
In Angular2, we have a component that encapsulates both a directive and a controller’s capability to expose variables to templates. A component is defined as a class with a @Component
decorator.
// app.ts
// imports
@Component({
selector: 'coffee-machine',
template: `
<p>Have a nice cup of {{name}}</p>
`
})
export class App {
constructor() {
this.name = 'coffee'
}
}
Directives and Local Variable Syntax
Local variables are now defined using the let
keyword. Structural directives like ng-repeat
and ng-for
are now prefixed by an asterisk, and all directives should be written in camelCase:
Angular 1.X
<ul>
<li>
ng-repeat="coffee of coffeeList">
{{coffee.name}}
</li>
</ul>
<p ng-class="highlight" ng-if="coffee.isReady">Ding!</p>
Angular2
<ul>
<li ngClass="highlight" *ngFor="let coffee of coffeeList">
{{coffee.name}}
</li>
</ul>
<p ngClass="highlight" *ngIf="coffee.isReady">Ding</p>
One-way Data Binding
Angular 1.X used ng-bind
directive to bind a property to a value. In Angular 2, this is done by placing the [property]
attribute in a tag.
Angular 1.X
<input ng-bind="coffee.name"></input>
Angular 2
<input [value]="coffee.name"></input>
Two-way Data Binding
The ng-model
directive used in Angular 1.X is replaced by the [(ngModel)]
directive in Angular 2.
Angular 1.x
<input ng-model="coffee.name"></input>
Angular 2
<input [(ngModel)]="coffee.name"></input>
Event Binding
Event binding attributes have also changed. Now they have to be enclosed in (parentheses), or have the on-
prefix instead of ng-click
.
Angular 1.x
<button ng-click="dispenseCoffee()"></button>
Angular 2
<button (click)="coffee.name"></button>
// or
<button on-click="coffee.name"></button>
Routing
Angular 2 release candidates have gone through several iterations of routers. Explaining all the intricacies would require writing a separate article, but we’ll try to scratch the surface.
Angular 1.x
Let’s remember how we used to do it in Angular 1.X, via an injected $routeProvider
service from the ngRoute
module.
// app.js
angular.module("coffeeModule", ["ngRoute"])
.config(function ($routeProvider) {
$routeProvider
.when("/coffee", { templateUrl: "coffee.html", controller: "coffeeController" });
})
.controller("coffeeController", function ($scope) {
$scope.message = "Eat at Joe's";
});
Angular 2
With Angular 2’s new router, we define routes in an external module. We define the path, name, and associate a component with the route.
// app.routes.ts
import { provideRouter, RouterConfig } from '@angular/router';
import { CoffeeComponent } from './coffee.component';
const routes: RouterConfig = [
{
path: 'coffee',
name: 'Coffee Home'
component: CoffeeComponent,
},
// default route definition
{
path: '',
redirectTo: '/coffee',
pathMatch: 'full'
},
];
export const APP_ROUTER_PROVIDERS = [
provideRouter(routes)
];
<!-- index.html -->
<a href="#" [routerLink]="['/coffee', coffee.name]">
Summary
By now it should be pretty obvious that Angular 2 is a completely different and reworked framework. Because of the sheer number of differences, comparing the two is a bit like comparing apples and oranges. Hopefully this article provided some food for thought on the nature of Angular 2, as well as the main differences between the two major versions.
Let’s do a wrap-up:
- Angular 2 is a completely different framework, rewritten from the ground-up, and is not backwards-compatible with the previous versions of Angular.
- It supports runtime-independent instances which allow more straightforward integration in mobile and native apps.
- Angular 2 is written in Typescript and official documentation examples emphasize the use of Typescript, although developers can (and probably will continue to) use ES6, ES5 and Dart.
- The syntax for structural directives, local variables and event bindings have changed.
- Angular2 has greater performance (at least 5x, and even more in some corner cases) due to the reimagined update loop (aka Change Detection).
- A brand new router.
- No more controllers and directives, but instead a component-based architecture.
The team behind Angular has boldly decided to completely overhaul it. Whether we like it or not, these changes will push us, as adopters, to consider things like Typescript and Web Components more closely, as there’s no denying the gravitational pull of decisions from giants like Google on the modern Web ecosystem.