If you have been doing some serious client-side development and/or are a fanboy of Angular, Angular 2 is something you would be watching carefully with excitement and a fair bit of skepticism. Angular 2 is garnering a good amount of attention with the Angular team steadily releasing new bits on Angular 2.
Note: This post is not about what’s new in Angular 2. Instead the focus of this post to provide suggestions that can be adopted today to make the transition migration from Angular 1.x to 2 less painful. |
Remember Angular 1 was a paradigm shift for most of us (especially developers with jQuery background). With Angular 2 we pivot again. The shift is towards embracing the new web standards such as Web Components. Backward compatibility in Angular 2 has taken a back seat as v2 drops some of the core concepts of Angular 1, including scopes, controllers, and directives.
With Angular 2 announcement, there was an apprehension that Angular development on the 1.x branch will stall with the focus shifting to v2. That has not been the case, as Angular 1.4.0 rc1 is just out and there are plans for v1.5 too.
Now that both Angular 1.x and Angular 2.x being actively developed, the technology adoption patterns have become interesting. Depending upon whether we are starting a new engagement or have already been developing on Angular 1.x we need to make some decisions.
Existing Angular projects
What options do we have if we have been developing on Angular 1.x for a good amount of time?
Plan and prepare for Angular 2 migration
Since Angular 2 is still in alpha, at this juncture the actual migration should be avoided. We have a wait for a stable API, yet prepare for the inevitable. The best thing that can be done now is to design and implement any new app feature in a manner that makes this migration easier. This post is all about detailing this very idea.
The problem with this piecemeal migration of code, of course, is that there are now more than one frameworks that we need to deal with. While this scenario may be supported by the Angular framework this may be a sub-optimal approach if there is too much to migrate.
Stay on 1.x branch and continue to develop with Angular 1.x.
As the saying goes “If it ain’t broke, don’t fix it”, we can continue building components with Angular 1.x, a perfectly acceptable solution. Since Angular 1.x is still actively development, we are in safe hands.
Said that, we should still adopt the practices guidelines that have been laid out to support easy v2 migration. These are some great design guidelines that can help us improve the overall Angular 1.x code structure. And in future allowing a relatively easier migration path to Angular 2.
The key take away here is:
Even if we plan to stay on 1.x branch the guidelines that are detailed in this post are worth understanding and adopting.
Starting something new
Things become more interesting if we have just started or plan to start the SPA journey? Angular 2 is not ready for prime time yet, and investing in an older technology when an overhaul is imminent may not be a great idea.
Options? We can ditch Angular and look for some other framework. Although in such a case we need to keep in mind that the framework of our choice supports the new web standards (such as web components) and is evolving in the direction where the browser-based technologies are moving.
But if you love Angular (as I do) and don’t want to give up on it yet, then the choice we have is same as described in the sections above.
Start development in Angular 1.x in a manner that allows easier migration to Angular 2 once it is released. Such migration may not be trivial process and Angular team understands this. To make this migration easier, the Angular team has been publishing some guidelines (see this video from ng-conf 2015) and more so will appear as new components are developed in Angular 2.
Let’s understand how can we develop in Angular today keeping in mind Angular 2.
Building today for tomorrow
This blog post is not about Angular 2. Instead what we are interested in is, what is gone! In Angular 2:
- Controllers – Gone!
- Directives – Gone!
- Scope – Gone!
- Modules – Gone!
- jqlite – Gone!
It seems like someone has pulled the rug from under our feet! There is not much left of the original Angular 1.x. But there is still hope! Let’s see what some eternal optimists like us can do today.
Embrace ES6 (even better, learn TypeScript)
ECMAScript 6 is scheduled for release pretty soon with a promise of a better JavaScript language. ES6 has a number of extensions that try to address the language quirks and make it easier it use.
Given that Angular 2 itself is now been written in TypeScript, the push apparently is towards the modern web programming languages.
With ES6 we can effectively utilize the new language features to improve to overall Angular code-base. We can do things such as:
- Implement any constructor function as class. In Angular this implies the standard, controller and service implementation can be replaced with class construct. A standard controller declaration:
angular.module('app', [])
.controller('HomeController', function (SomeService) {
$scope.mesage = "Hello World";
});
now becomes
class HomeController {
constructor(SomeService) {
this.someService = SomeService;
this.message="Hello World";
}
}
angular.module('app', [])
.controller('HomeController', HomeController);
- Use arrow function for all types of callback requirements, which has a much terser syntax. Arrow functions also share the same lexical
this
as their surrounding code, that further reduces confusion when in comes to this
resolution. Look at this code with callbacks:
return this.$http.get(url).then(function (response) {
return response.data.photos.photo.map(function (img) {
return {
title: img.title,
url: 'https://farm' + img.farm + '.staticflickr.com/' + img.server + '/' + img.id + '_' + img.secret + '_z.jpg'
};
});
});
and this is the same code with es6
return this.$http.get(url)
.then(response =>
response.data.photos.photo.map(img => {
return {
title: img.title,
url: `https://farm${img.farm}.staticflickr.com/${img.server}/${img.id}_${img.secret}_z.jpg`
}
})
);
- Use string interpolations, which have a much nicer syntax. Code such as this:
return '' + name + '/' + name + '.tpl.html';
becomes
return `${name}/${name}.tpl.html`;
There are number of other enhancement to the language that we can readily use within our existing Angular (JavaScript) code.
To consume ES6 features today, we need tools such as babel or Traceur that transpire the ES6 code into standard JavaScript (ES5) before loading the scripts in the browser. The additional step of transpiring ES6 code needs to be handled during the build process. This post provides extensive coverage on this topic.
IDE support for ES6 is another dimension that we need to consider. Some IDE’s are lagging behind in this area, notable among them is Visual Studio that does not support ES6 syntax as of now.
Use the controller as, better still get rid of ng-controller
controller as
syntax (with ng-controller
) has been part of Angular since v1.2. It still has missed the radar of sizable number of Angular developers. controller as
allows us to program the controllers at a higher level of abstraction without involving scopes directly.
Look at this succinct example highlighting the above approach:
<div ng-controller="UserController as user">
<div>{{user.name}}</div>
</div>
app.controller("UserController", function() {
this.name="Bob";
});
Clearly the controller does not need to reference the $scope
object in UserController
. Instead of adding properties to $scope
, they are added to the controller’s constructor function. Under the hood, Angular just adds the controller instance as property (user
) to the newly created scope.
This approach has some notable advantages:
- It minimizes scope pollution as everything is now declared on the controller.
- Solves the problem of prototypal reads vs writes quirks, as we now use `.` notation across all our bindings (such as
user.name
).
- Minimizes parent-child scope dependencies that arise when child scope intentionally or otherwise refers to the parent scope properties. With controller as syntax, the only reference added to scope is the controller reference.
The icing on the cake is that the code now is better suited for Angular 2 migration. You can learn more about controller as syntax by following this post for Todd Motto.
controller as approach is also supported via $routeProvider with property controllerAs . The net effect is same. |
We can go a step further and actually avoid using ng-controller
directive itself!
Directives in Angular too support controller as
syntax to define controller via the directive definition object property controllerAs
. Even isolated scopes variable can be directly added to controller by setting bindToController
to true.
This usage of ng-controller:
<div ng-controller="UserController as user">
<div>{{user.name}}</div>
</div>
is replaced with:
<user-details></user-details>
This suggestion may seem counter-intuitive, as we have been using controllers with route configuration and ng-controller
right from when we started learning Angular, and controller is one facet of the MVC triad. Well, we are not abandoning controller per se, instead the controller now becomes part of the directive.
Instead of me going into the benefits of this approach I will point you to this great post by Tero Parviainen.
Angular 2 architecture is based on creating components and linking them together. These components are HTML elements that come with their own template (view), behavior and can bind to data. By getting rid of ng-controller
and creating directives that behave like components, it becomes easier for us to migrate such directives to Angular 2 components later.
Use the Angular New Router
The Angular New Router is part of Angular 1.4 and shares its code base with Angular 2. The new router is significantly different from the standard routing service ($route
) that is part of core Angular modules.
If you have been building our apps using the standard $route
and ng-view
directive, the new router is a far superior alternative, and is worth trying out. This feature-rich router supports number of routing scenarios such as:
- Nested routes.
- Sibling routes.
- Route activation life-cycle.
The notable thing about this routers it that there is no $scope
injection altogether as a new scope is not created as part of route resolution.
Angular New router does create a new scope and that scope is injectable. There was a bug in injecting the scope in an older version which has now been fixed. This led me to believe that scopes are not injectable with new router.
While writing this post, there are number of issues with Angular New Router that are still being addressed. Things still need to be ironed out before the new router can be consumed effectively. I hope this happens soon. |
To learn more about the Angular New Router I will recommend this excellent blog post by Jurgen Van de Moere.
Stay away from scopes!
Minimizing direct access to scopes from a implementation perspective is a good thing. If we follow the advice from the last two sections, we are complying with this requirement.
Remember we want to avoid accessing scope directly, but the underlying 1.x framework still makes extensive use of scopes to do all type of model-view synchronization. |
We can almost get rid of $scope, except for two scenarios
- If we are using
$scope.$watch
- Or we are using Angular events (
$emit
, $broadcast
and $on
)
$scope
injection hence becomes imperative, whenever we want to watch model mutations or want to publish and subscribe to events. I am not aware of any workaround that exist to circumvent these limitations.
The uncharted territory
While we have covered some significant area in Angular 1.x and how to handle breaking changes. There are still area that need varied degree of attention. These include
- Views: The HTML template views that Angular 1.x uses is different from Angular 2 component views. Number of event directive such as ng-click, ng-key*, ng-mouse*, plus directives such as ng-show, ng-hide are not there in Angular 2.0. Even the view template syntax is new in Angular 2. Maybe a HTML processor can be written that can automate the process (partially or completely) of converting an Angular 1.x views to Angular 2.
- config and run blocks: These are blocks that run during the bootstrap stage of the app. While I don’t know the exact counterpart of config and run block in Angular 2, albeit migrating such a code should not be difficult.
- Modules: Angular 2 uses ECMAScript module infrastructure instead. Need to understand what would be required to minimize migration effort here.
- jqlite: jqlite is gone! Given our investment in building a plethora of directives I am not sure where we are headed in terms of jqlite.
To conclude, Angular 2 still has a lot of moving parts that need to stabilize before an effective migration strategy can be defined. This post is based on my research and understanding of where Angular is moving. As and when more details start to emerge I will update this post with the relevant content.
And just to reiterate, the guidelines detailed above can be adopted irrespective of whether we want to migrate to Angular 2 or not. Applying these practices definitely will improve the overall quality of Angular 1.x code.