Angular 1 vs Angular 2 - An In-Depth Comparison

Complete Typescript Course - Build a REST API
icon
(Click the image to view the course)

Updated to Angular 2 Release Candidate

The main functionality and the core documentation of Angular 2 are both already available. Let's gather here what is so far known about the design goals and features of Angular 2, and how they compare to Angular 1:

  • 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):

  • in the Javascript runtime, everything is patcheable by design - we can change the Number class if we need
  • Angular at startup will patch all points of asynchronous interaction:
    • timeouts
    • Ajax requests
    • browser events
    • Websockets, etc.
  • At those interaction points, Angular will run dirty checking on the scope objects to see if changes occurred and if so trigger the corresponding watchers
  • re-run dirty checking to see if more change changes occurred, and re-run watchers, etc.

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

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 there will be only one DI mechanism: constructor injection by type.

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.

Thi 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.

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 Academy 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:

comments powered by Disqus