The main functionality and the core documentation of Angular are both already available. Let's summarize here some design goals and features of Angular, and how they compare to AngularJs:
- Mains goals of Angular
- Simpler to reason about
- Angular 1 vs Angular 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
The main goals of Angular
The main goal of Angular 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.digest, which was not always straightforward
- on occasion, we had to call
$timeoutto let Angular finish its digest cycle and do some operation only when the DOM is stable
In order to make Angular easier to reason about, one of the goals was to create more transparent internals that works 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
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
- Instead, AngularJs will use a different version of that method, and the same goes to:
- Ajax requests
- Websockets, etc.
- The way that that special version of
addEventListenerworks, 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 were registered using the corresponding AngularJs
$timeoutetc. own directives and services
- Any events registered with plain
setTimeoutwill not call dirty checking, and will therefore need to integrate with AngularJs by manually calling the digest cycle via
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:
- it's 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 an understanding of AngularJs internals
One of the first steps that the Angular team took in the direction of Angular, was to extract from the Angular code base the mechanism of patching all asynchronous interaction points and made it reusable.
The result of such refactoring is Zone.js, which can be compared to the equivalent of a thread-local context in Java.
How Angular is more transparent due to Zones
$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 were made in both Angular 1.3 and Angular 1.4.
But it's 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), it's better to refer to this podcast and blog post. I'll try to summarize here the two main reasons why Angular is much faster:
Faster checking of a single binding
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 will allow the developer to provide some guarantees to the change detection mechanism to avoid scanning parts of the component tree. 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 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 to 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 there is the goal to unify these mechanisms into a single mechanism, for reducing the learning curve and improve readability.
How will Angular DI improve the situation
In Angular 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 its parent injector and so forth. This sets the ground for providing native lazy-loading support in Angular.
Goal: Web Component friendly
The future of the web is Web Components, and Angular wants from the start to play well with future web component libraries. For this, one of the goals of the Angular template syntax is to keep attributes clean and don't put any Angular expressions on them - everything is bound 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
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 will interact better with Web components
In Angular, the template syntax will avoid binding to plain attributes, unless to read constants:
[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
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 will support it via 3 different mechanisms:
default mode: by default, the Shadow DOM is not on and the internals of the component 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 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 will allows supporting 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 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
One of the goals of Angular is to provide a clear migration path from Angular 1. This will only become clear when Angular is near it's initial release, but for now the following is foreseen in the ng-upgrade project:
- the new Angular router is being backported to Angular 1, and will allow the same application to have both Angular 1 and Angular routes
- It will be possible to mix Angular 1 and Angular 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
I'm really excited about Angular, 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?
If you want to learn more about Angular, have a look at the Angular for Beginners Course:
Other posts on Angular
If you enjoyed this post, here some other popular posts on our blog:
- Angular Router - Extended Guided Tour, Avoid Common Pitfalls
- Angular Components - The Fundamentals
- How to run Angular in Production Today
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular Forms - Template Driven, Model Driven or In-Between
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
- How does Angular Change Detection Really Work?