AngularJs vs Angular 2 - An In-Depth Comparison

This post is up to date with the Angular 2 Final Release

The main functionality and the core documentation of Angular 2 are both already available. Let's summarize here some design goals and features of Angular 2, and how they compare to AngularJs:

  • Mains goals of Angular 2
  • Simpler to reason about
  • Angular 1 vs Angular 2 change detection
  • More transparent internals with Zones
  • Improved stack traces
  • Much improved performance (and why)
  • Improved modularity
  • Improved Dependency injection
  • Web component friendly (how and why)
  • Support for Shadow DOM
  • Support for native mobile rendering in Android and iOs
  • Support for server side rendering
  • Improved testability
  • Migration path to Angular 2
  • Conclusions

The main goals of Angular 2

The main goal of Angular 2 is to create a web framework that is super easy to learn and just works. Let's see how this is planned to be achieved:

Goal: Simpler to reason about

In the current version of Angular, we sometimes have to reason about the framework internals for certain use cases, such as having to reason about application event initialization and the digest cycle:

  • In Angular 1 there was no digest cycle finished event (see reasons for the new Forms module), because such event might trigger further changes that kept the digest cycle going.
  • we had to reason about when to call $scope.apply or $scope.digest, which was not always straightforward
  • on occasion we had to call $timeout to let Angular finish its digest cycle and do some operation only when the DOM is stable

In order to make Angular 2 easier to reason about, one of the goals was to create more transparent internals that work out of the box.

To start, let's have a look at how the Angular 1 binding mechanism is implemented, and how it will be made more transparent.

How Angular 1 implements binding

In Angular 1, the ng-model functionality of being able to edit a form and instantly have those changes reflected on a Javascript POJO is one of the main reasons why the framework became so popular.

The way this works in Angular 1 is the following, according to this podcast (see at 3:50):

  • When registering a click handler or any other event in a template, Angular will not just use the browser default addEventListener
  • Instead, AngularJs will use a different version of that method, and the same goes to:
    • timeouts
    • Ajax requests
    • Websockets, etc.
  • The way that that special version of addEventListener works, is that first the browser event is triggered but then right after Angular will run dirty checking on the scope objects to see if changes occurred and if so trigger the corresponding watchers and update the template
  • re-run dirty checking to see if more change changes occurred, and re-run watchers, etc.
  • This means that templates will be kept in synch with the model automatically, but only for event handlers, timeout handlers etc. that where registered using the corresponding AngularJs ng-click, $timeout etc. own directives and services
  • Any events registered with plain addEventListener, setTimeout will not call dirty checking, and will therefore need to integrate with AngularJs by manually calling the digest cycle via $scope.$apply() or $scope.$digest()

Get our Free Angular 2 For Beginners Course

(click the image below to get access)

Consequences of how binding works in Angular 1

The result is that the DOM is kept permanently in sync with a POJO and it just works, but all this can sometimes be hard to reason about:

  • its not clear which watchers will be fired and in which order, or how many times
  • the order of the model updates is hard to reason about and anticipate
  • the digest cycle can run multiple times which is time consuming
  • integration with 3rd party libraries often requires an AngularJs module wrapper that calls $scope.$apply() when the library event occurs, or the application developer needs to make sure this is done at the application level which requires understanding of AngularJs internals

One of the first steps that the Angular team took in the direction of Angular 2, was to extract from the Angular code base the mechanism of patching all asynchronous interaction points, and made it reusable.

Introducing Zones

The result of such refactoring is Zone.js, which can be compared to the equivalent of a thread-local context in Java.

It can be used for many use cases, such as for example allowing the framework to generate long stack traces that span multiple Javascript VM turns.

How Angular 2 is more transparent due to Zones

Angular 2 uses the zones mechanism to make a lot of the reasoning about the digest cycle no longer necessary. This plain non-Angular specific Javascript code will transparently trigger a Angular 2 digest, if triggered by a component that is inside a zone:

No longer $scope.apply or $scope.digest are needed, everything transparently works. Probably we won't have to reason about zones in general in most use cases, and it will still be possible to run code outside the Angular zone by using VmTurnZone.

Goal: Improved performance

The above description of the digest cycle makes it clear that all this is can be time consuming, although a lot of performance improvements where made in both Angular 1.3 and Angular 1.4.

But its unclear of those performance improvements can go much further, one of the reasons for that is the possibility of the existence of change detection loops.

To better understand how the performance gains are achieved (up to 5-10 times faster last then Angular 1), its better to refer to this podcast and blog post. I'll try to summarize here the two main reasons why Angular 2 is much faster:

Faster checking of a single binding

The mechanism to check a single binding was optimized to allow the Javascript VM to optimize that code into native code via just-in-time compilation. Instead of scanning recursively a tree of objects, a function is created at Angular startup to see if the binding has changed.

This binding-checking function looks like a function that we would write by hand to test for changes and it can be easily optimized away by the VM.

Avoid scanning parts of the component tree

Angular 2 will allow the developer to provide some guarantees to the change detection mechanism to avoid scanning parts of the component tree. Basically the developer will have two options:

  • make the model an Observable: Angular will detect this and register itself to observe the model. This way if the model changes/remains the same Angular will know via the observable mechanism and so it does not need to run change detection for that object.

  • make the model immutable, using for example Facebook's immutable.js. Again the idea is that Angular will detect this and avoid running change detection on immutable objects.

Goal: Improved modularity

In Angular 1, the Angular modules are mostly dependency injection containers that group related functionality.

These modules are for example not asynchronously loaded based on their dependency listings the first time that a dependency is needed, like AMD modules for instance.

Angular 1 and module lazy-loading

Angular 1 lazy loading is still possible with a solution like ocLazyLoad, but ideally it should be something native to the framework and more transparent, and according to this podcast it seems that in Angular 2 it will be the case (see at 13:06).

Goal: Improved dependency injection

Angular 1 dependency injection is a leap forward in building more modular applications, but it has a couple of corner cases that cannot be fixed anymore without major breaking changes.

Angular 1 has a global pool of objects

One of the DI corner cases in Angular 1 is that there is only one global pool of objects per application. This means that if for example the main route loads backendService and we navigate to route B, we could there lazy-load other services specific of that route.

The problem is, let's say we lazy load a second backendService with a completely different implementation: it would overwrite the first one! There is currently no way to have two services with the same name but different implementations, which prevents lazy-loading from being implemented in Angular 1 in a safe way.

Angular 1 silently overwrites modules if they have the same name

This is a feature to allow for example to replace the service layer services with mocks at test time, but might cause issues if we accidentally load two times the same module.

In Angular 1 there are multiple DI mechanisms

In Angular 1, we can inject dependencies in multiple places in different ways:

  • in the link function by position
  • in the directive definition by name
  • in the controller function by name, etc.

In Angular 2 there is the goal to unify these mechanism into a single mechanism, for reducing the learning curve and improve readability.

How will Angular 2 DI improve the situation

In Angular 2 we will be able to inject component dependencies in the constructor:

The fact that there is only one mechanism makes it easier to learn. Also the dependency injector is hierarchical, meaning that at different points of the component tree it's possible to have different implementations of the same type.

If a component does not have a dependency defined, it will delegate the lookup to it's parent injector and so forth. This sets the ground for providing native lazy-loading support in Angular 2.

Goal: Web Component friendly

The future of the web is Web Components, and Angular 2 wants from the start to play well with future web component libraries. For this, one of the goals of the Angular 2 template syntax is to keep attributes clean and don't put any Angular expressions on them - everything is binded via properties only.

To understand why this is important, take a look at this example:

Here we have an Angular 1 component that interacts with a future web component library.

What is the problem here?

Well, the web component behaves just like a browser component, such has for example the img tag.

Therefore at page startup time and before Angular gets a chance to kick in, the Angular expression will be passed to the component which would act upon it directly, like the image element immediately loads the image using the url provided.

This is actually the reason why all the attributes like ng-src are needed, to work around this issue.

How Angular 2 will interact better with Web components

In Angular 2, the template syntax will avoid to bind to plain attributes, unless to read constants:

The [setting] is a property binding that will write a value of an expression into a component property. In no place will there be Angular expressions in plain attributes, to prevent interoperability problems with web components.

Support for Shadow DOM

One of the key features of web components is the Shadow DOM. This is a native browser mechanism that will allow to build native looking components, say a new implementation of select.

A web component can still be implemented in plain HTML/CSS but isolated from the main page, in some ways in a similar way to as if it where inside an iframe with a separate document root.

Because the Shadow DOM is currently only implemented in Chrome, Angular 2 will support it via 3 different mechanisms:

  • default mode: by default the Shadow DOM is not on and the components internals show up in the same document tree as the main page

  • emulated Shadow DOM: the Shadow DOM CSS isolation mechanism can be pollyfiled using Polymer by having the CSS inside the component being prefixed on the fly to make the CSS more specific

  • true Shadow DOM: as mentioned this only works in Chrome

Goal: support for native mobile - iOS and Android

Angular 2 will have two layers, the application layer and the rendering layer. For example a component can be annotated with different @View annotations that can be enabled at runtime depending on the environment.

In a similar way to React Native, Angular 2 will allow to support the notion of:

Learn once, write anywhere

This means the knowledge of Angular can be reused to build native applications as well as web applications, although there will always be some differences.

Goal: support for server side rendering

Single Page Applications have been around for a while, and any web site with a navigation system could benefit from being built that way.

This is due to the increased user engagement that comes from being able to navigate effortlessly around the site without having to reload everything again.

But Single Page Apps had at least until recently a very strong SEO cost, making them unpractical for anonymously served public content. SPAs have been so far used only to build the "dashboard" part of public internet apps due this. More info about Angular Universal in this post.

See Server Side Rendering in Action

With Angular Universal we will be able to build SPAs that render on the server as plain HTML and so are very SEO friendly, but still take over on the browser as a single page application.

As an example to see what that looks like, take the Angular University website itself, for example this page. If you click around you will see that its a SPA and navigation feels instantaneous. But take a look at the network tab: all initial content was rendered server side.

This will allow us to build a whole new category of apps: engaging and effortlessly navigatable but still 100% SEO friendly.

So far mostly only companies like Facebook could pull server-side rendered apps off at the expense of having a very highly specialized engineering team. Today with the Angular Universal Starter this type of apps become much more viable.

Goal: Improved testability

In Angular 2 it's relatively hard to write true unit tests, because for example ng-model really needs a DOM to be tested which leads to use of solutions like using PhantomJs.

The problem with this approach is that those tests are no longer unit tests, they are integration tests which have the following issues:

  • they are slow to execute
  • they are fragile and harder to maintain

This leads to an inversion of the test pyramid, which is a situation where most of our tests are either UI tests or integration tests, and there are hardly any true unit tests. This means the build constantly breaks by reasons other than true bugs and the testing effort pulls less of its weight than what we would want.

The introduction of a separate rendering layer will allow to make unit tests super fast and with minimal dependencies, making them easier to write and maintain and allowing to run them much more often.

Goal: migration path to Angular 2

One of the goals of Angular 2 is to provide a clear migration path from Angular 1. This will only become clear when Angular 2 is near it's initial release, but for now the following is foreseen in the ng-upgrade project:

  • the new Angular 2 router is being backported to Angular 1, and will allow the same application to have both Angular 1 and Angular 2 routes
  • It will be possible to mix Angular 1 and Angular 2 components in the same application
  • It will be possible to inject services across framework versions
  • data binding will work accross versions as the two change detection mechanisms will be integrated

Conclusions

I'm really excited about Angular 2, after having tried it out with a few components, I can see how its easier to learn and more transparent to the developer. Again much of the things described in this post such as Zones will just work, and the integration with 3rd party libraries is much improved.

Want to try it out?

It's definitely not too soon to try it out, if you want to give it a go this is a seed project, and the Visual Studio Code editor or Webstorm already provide great Typescript support.

Also have a look at this ongoing YouTube course for Getting Started with Angular 2. It contains an introduction on components and directives, but also contains a lot of useful tips like debugging techniques via the console or Augury:

Looking for other Angular 2 Learning Resources ?

Check our top 10 list of Angular 2 Resources:

Top 10 Angular 2 Tutorials, Blogs and Podcasts - And How to tackle Javascript Fatigue

Other posts on Angular 2

If you enjoyed this post, here some other popular posts on our blog: