Skip to main content
Blog

Angular 2.0: Opinions, Commentary, and a Migration Case Study

by Jeremy Likness

Angular is a framework designed to make it easier to build client applications (that is, apps written with JavaScript and HTML that run on various devices, including mobile devices and browsers). Business users often expect a rich user experience and don’t like to compromise on features just because the app is delivered over the web or via a mobile device instead of installed on a desktop. For this reason, an approach to building apps called Single Page Applications (SPA) has evolved. Angular provides built-in support for SPA via templates, components, services, and tools that help developers call web services and manipulate the data that is returned.

The most common question I’m asked at events when I speak about Angular is, “What do you think about Angular 2.0?” This blog post is my answer to that question.

Angular has been available for several years now and is currently in a stable 1.5 version release. Not too long ago the Angular team announced they would release a major version, 2.0, and began work in parallel with the current 1.x releases. The initial announcements that Angular 2.0 would be a complete re-write without backwards compatibility created major concerns in the development community. Later, the Angular team announced they would release a module to support running Angular 1 and Angular 2 modules “side-by-side” to support an upgrade path. Although this makes it easier to reuse existing code, developers will probably end up migrating part or all of their code to Angular 2.0 to take advantage of its latest features.

The following bullets summarize some of the major changes between Angular 1.x and Angular 2.0:

  • Written with the modern browser in mind, Angular 2.0 is developed using TypeScript, a superset of JavaScript that supports future specifications but compiles into plain JavaScript that will run on current browsers. You can also use ECMAScript 6 (more recently referred to as ECMAScript 2015, the most current specification), and leverage tools like js to transform your code.
  • Angular supports a declarative markup syntax for applying metadata to classes that allows you to “tag” JavaScript codes with attributes such as what template or “view” to use for the code and how to instantiate it using HTML markup.
  • Angular 2 introduces changes to dependency injection, or how components that reference each other are “wired up.”
  • There is an entirely new approach to controllers, directives, and filters (I’ll explain more of what these are and how they changed).

There are other changes as well that I didn’t dig into, such as how services like routing and the popular $http service for making AJAX calls may have changed. The reference project didn’t dive that deep but was a great way to ease into what to expect.

The Source App

The source for the migration is the Angular Health App I wrote to demonstrate how Angular apps are built. The final result is an application that features multiple controllers that share information and state and an interactive interface to calculate certain health-related values such as target heart rate and basal metabolic rate. The app is also responsive and scales to different screen sizes (try it on your mobile phone!) The GitHub repository link and related deck give you everything you need to know to pull down the application and step through multiple iterations to see a test-driven development lifecycle unfold. You can see the final application in action here.

migrating angular 1.x applications to angular 2.0

The app follows my philosophy of avoiding dependencies on $scope, $watch, and built-in broadcast functionality. You can see that despite the interactions between multiple controllers and services (for example, you can switch the unit of measure or change the age and see it reflected immediately in the tiles) there is no explicit $scope dependency and no need to call $watch to determine when changes took place. This is all explained in the articles I linked to earlier.

It took me overall just a few hours to port the application entirely over to Angular 2. You can view the Angular 2 migration source code and follow the directions to run it locally.

Migrations

Working with Angular 2 has given me several insights that I’ll relate later in this post. First, I want to talk about the migration process itself. In this section I take an objective look at the migration and the correlation between Angular 1.x features and their Angular 2 counterparts.

HTML

HTML is the markup that drives the UI of the application. One reason that I love Angular is because it enables extending markup and applying behaviors. I discuss this is more detail in my blog post, “The Modern HTML5 Answer to MVVM.” Markup is easy to learn and understand and most people can get a grasp on what markup is long before they are able to write code. Ideally markup will undergo minimal changes provided the way to expose behaviors and extend markup is consistent. In other words, if I create a widget, I should be able to declare the widget in my UI in a similar way even if I have to rewrite the way it is implemented in code. Migrating the HTML for the app was relatively straightforward. Although I did have to make changes due to the differences in syntax for data-binding, it was more of a “cut, copy, then modify a few items” workflow. This was not exhaustive and I believe will scale well even with large applications. Here is the markup to input your age in Angular 1.x:

Here is the same markup in Angular 2:

Most of the changes are with the syntax for binding. They may seem obscure at first, but the goal is to leverage as much of the existing markup as possible (such as “class”) and then annotate that markup to indicate intent. Digging into the nuances of how that works is beyond the scope of this post.

Here is the source for a tile to display Body Mass Index (BMI) with the tile highlighted by a warning color when someone is outside of the healthy range:

And this is the same tile in Angular 2:

In Angular 2, classes tagged as “components” are automatically bound to corresponding “views” so there is no need to declare a controller or bind to a property using “dot notation.” The Angular 2 binding syntax allowed me to link (data-bind) my custom classes directly to the class attribute without relying on a special “ng-class” directive.

Common Code

I refer to “common code” as the logic that I should be able to reuse without relying on a particular framework. I see too many Angular projects inject logic directly into controllers and services, rather than writing vanilla JavaScript implementations that can be tested independently and used by controllers and services, rather than defined in them. For example, I created a Plain Old JavaScript Object (POJO) to convert between metric and imperial measurements. This code will work in any framework as it doesn’t have any “Angular-specific code.”

In Angular 2, I took the existing JavaScript “as is” and dropped it into a TypeScript file, then added an “export” for it so it is available through the type system to other modules. This was accomplished with a single keyword:

export var Conversions = (function () { 

To make it easier to reference the component throughout the application, I also added an interface. This isn’t required for Angular 2 and isn’t a part of the conversion, but it is something I did to take advantage of TypeScript capabilities.

Now I can easily reference the interface in my components but also mock it for purposes of testing as needed.

Services

In Angular 1.x the method for managing dependencies depended on containers called modules that are managed by the Angular runtime. To make your code available you had to register it with Angular. For example, this is the service registration for the conversion object. Notice it simply passes the type constructor and registers it with the container using a friendly name.

In Angular 2.x the TypeScript language supports exporting types and classes and imports them directly so there is no need for a service wrapper. Instead, the type is imported directly from the file and dynamically loaded when needed. This is the code to import the type:

The TypeScript compiler will keep track of the imports and load them using Asynchronous Module Definitions (AMD) as needed.

Controllers

One of the major changes in Angular 2 is how the framework deals with controllers. In Angular 1.x you had the option to create a controller that operated directly on a $scope object. The $scope object acted as your “view model” or an object with a set of properties and functions used for data-binding. The team later released a feature called controller as that allowed the controller object to be the view model itself. In other words, instead of acting like a traditional controller that manages the data-binding, the controller becomes the data-binding object itself. I’ve argued often that this is the preferred way to approach Angular apps because it allows you to focus on pure presentation logic without a strong affinity to the framework, making it easier to test and potentially migrate your code.

Angular 2 embraces the concept whole-heartedly and gets rid of the need to have a special type of controller. Instead, any class can be exposed as a component that is bound to a corresponding view. A special decorator is used to “tag” the class with metadata to describe how it will be used in the framework. For apps that relied heavily on $scope this will require quite a bit of refactoring. With the newer controller as functionality, the migration is more straightforward. Here’s the controller to manage data-binding the formula results in Angular 1.x. Note the dependencies are defined in both the constructor and the controller registration at the end of the snippet:

In Angular 2, the formulas aren’t wrapped in a service and can be imported directly. Notice the class is defined almost identically to the Angular 1.x controller, just simplified to use the new TypeScript syntax to define the getter and setter properties. The @Component attribute is used to specify how the component is invoked in markup and what template is used to render it.

I’ve been urging developers to embrace controller as for a long time now and it looks like that advice will pay off when migrating to Angular 2.

Filters

The final piece that I converted was filters. These are simple functions that take an input value with optional arguments and transform it to formatted output. For example, the date filter converts a date object to a readable format based on the user’s locale, and a currency filter will take an ordinary number and display it using currency symbols. In Angular 1.x I created a filter to transform a Body Mass Index (BMI) value into a human-readable zone (underweight, normal, overweight, or obese). The filter looks like this:

In Angular 2, the functionality is very similar but is referred to as “pipes” because the pipe symbol is used to invoke the filter. An example of the pipe symbol was shown in the BMI tile snippet embedded earlier in this post.

The filter migration was a relatively painless “cut and paste” operation with a few tweaks to add the attribute and leverage TypeScript.

Lessons Learned: Angular 1.x Code

After going through the exercise the question to answer is, “What could I have done differently in Angular 1.x to make the migration easier?” I’ve already pointed out that I embrace an approach of focusing as much on independent JavaScript classes and objects as possible – i.e. creating a standalone model for properties and functions that doesn’t rely on Angular constructs like $scope. I still believe this is the ideal approach and it served me well with the migration. Almost all of the “business logic” (such as conversion methods and formulas to calculate the various health indicators) migrated “as is” with little to no change. Most of my “view models” or controllers also carried forward. I did have to learn the new syntax for data-binding in the templates and how to annotate the classes, but otherwise it was a straightforward migration and would have been far more difficult had I used code that relies heavily on $scope.

The biggest change I could have made was to build the original app in TypeScript. I created it as part of a walkthrough I did for a local conference, so I didn’t want to muddy the waters by explaining Angular and TypeScript. However, the fact that TypeScript is really a superset of JavaScript and provides so many more features that make it easier to code is a strong argument in my opinion to use it all of the time. You don’t have to use TypeScript to write Angular code but it makes it far easier and provides you advanced capabilities such as development-time type checking and easier discovery. I believe the migration could have gone about 30% faster had the code been in TypeScript to begin with. I used to suggest TypeScript for larger teams and apps but even as a “team of one” I’ve found that most modern development environments leverage TypeScript out of the box for a very streamlined development experience. I used tasks when I was building the Angular 2 version that automatically compiled the TypeScript to JavaScript as I saved files and refreshed the website in the browser so I had real-time validation of my changes.

The Good

There is certainly a lot to like about Angular 2. The development environment is first class when using TypeScript because definition files give you easy access to the API. It leverages a lot of built-in capabilities of the language and from what I’ve seen and read performance is great. In some ways it simplifies the process of building various components. There is a clear upgrade path that allows you to choose whether you want to use some of the directives that enable side-by-side running of Angular 1.x apps or convert your code to run entirely in Angular 2. It is being built with documentation in mind so there is already a comprehensive site with tutorials, guides, and API descriptions you can plug into for development. The new data-binding syntax actually resulted in the ability to eliminate dozens of built-in directives in favor of a more comprehensive way to update attributes.

The Bad

Having said that, I do have my reservations about Angular 2 and want to perform a similar migration exercise with other frameworks like React and Aurelia for comparison. Although the decorator syntax looks nice at first, it does introduce a bit of affinity between the code and the framework. The suggested approach is to simply annotate the class directly and even add annotations to parameters in the constructor to map them back to their definitions. In my opinion this makes it more difficult to create clean, reusable classes that could be migrated to other (or newer) frameworks. Here is the start of a definition for the profile component in the Angular 2 app. Everything in the component definition is necessary to get it to work, and the constructor also requires special inline annotations.

Contrast the with the definition in Angular 1.x – note that there is no ceremony around the constructor parameters so you could just as easily invoke them in a non-Angular framework and pass in mocked objects as needed.

function Controller (userProfileService, uomService, conversionService) {

The current tutorials also suggest specifying the view (as a template or URL pointing to a template) on the component. One of the benefits of specifying the component in the view in my opinion is reuse. A controller could potentially be reused across views that display or collect the information in different ways. I assume this feature may be available in Angular 2 but it’s not clear how you would leverage different templates for the same component.

Finally, the suggested approach to handling forms in Angular 2 is to use a special component called “forms builder.” I introduced this into the user profile to illustrate how it works. All of the properties are accessed via data-binding on the model using declarative markup in HTML except the property for weight, which is defined in the code like this:

To use the form object, the template must create a link back into the form using the ngControl directive:

The forms builder allows you to group controls, specify how they are bound to data and validated, and can even generate the HTML for the form on the fly for you. The argument is that this simplifies development and enables more advanced testing scenarios. My concern is that we lose the designer/developer workflow. The developer is now taking more control over the layout and functionality of the form, when fundamentally I believe a form is all about user interface and user experience and should be in the hands of the designer when one is available.

The alternative is to data-bind directly to a property and use directives for validation. I created directives to validate a range (the min and max you see are actually directives that replace the built-in HTML attributes) and then bound directly to the age property on the model using an implicit form rather than an explicit one created in code. Everything is handled by the markup and the model just deals with properties rather than being coupled to a form object:

It will be interesting to see how these scenarios scale as almost anything you read related to Angular 2 advocates the form builder approach. This brings up another point. Were you confused by what this does?

It took me awhile to grasp it. The # syntax creates a “local variable” you can use in markup. In this case, the local variable “weightCtrl” is defined and mapped to the current “ngForm” control which is the one Angular created for the input tag for weight. After creating the local variable, I can then reference properties on the control such as whether it is in a valid state and use a class to turn the border red if it is not. This is a very powerful and flexible feature, but also confusing. In my opinion it adds unnecessary complexity to the workflow for building out new forms, and will create a mess of HTML because it is unclear whether you are binding to properties on the component or locally declared variables.

The Ugly

Two of the biggest issues I have in Angular 2 are related to the suggested approach for importing modules. In Angular 1.x if I defined a filter it was available throughout the application. I could simply add that filter to markup and it was ready to go. With Angular 2, you have to specify the directives you are using on the component that needs them. That means I have to be constantly aware of where in the application hierarchy the modules are, explicitly import them, and explicitly declare the dependency on the component or it will fail. This includes functionality like pipes. In Angular 2, I was surprised to see the pipe I identified wasn’t being called until I added it to the component declaration. Finding the module and then declaring the dependency explicitly each time feels like a lot more work than I should have to do.

The imports also impact testing. The simple demos show how easy it is to change the import statement to pull in a mocked test object rather than the production version. That looks good on paper but I wonder how it scales to a larger project? Large projects will end up with a lot of import statements and probably end up requiring specialized solutions to make it easier to swap in or out mocked libraries without going through a lot of code to do it. I didn’t port over the tests related to the project so I’ll be able to share more once I research that area, but from what I’ve seen it is a concern of mine. In Angular 1.x you registered components and included them in a source path. In Angular 2 they are declared/registered throughout the code and explicitly linked to a specific path via the import instruction.

My biggest concern is simply the complexity of the Angular 2 app.  This is the hardest aspect to be objective about because I’ve been developing and architecting Angular 1.x apps for more than four years now so I’m very comfortable with them. I have also lead large teams developing large projects for several decades and I don’t just look at frameworks from the perspective of what they do or how well they do it. I want to know how easy they are to learn, how close they are to the “mainstream methods” for developing, and whether they facilitate building apps at scale (i.e. how easy it is for six people to work in a similar area of code at the same time.) Right now I have serious reservations with Angular 2 because it feels even more opinionated than Angular 1.x. I think there will be a far more significant learning curve even disregarding the jump to TypeScript and/or ECMAScript 6. Although the framework is more flexible, that also creates more opportunity for defects and errors to be introduced into the code base. There is a lot of responsibility placed on the developer to understand where specific components are to import them correctly, declare them in the right slot on the component attribute, then annotate them in the constructor when necessary.

Summary

I found it very informative to take an existing application and port it over to Angular 2. The process was a bit rough because I started when it was in alpha so there were breaking changes every other day. Fortunately the code base eventually stabilized and I had minimal overhead in updating it to the beta release. As a developer I’m very excited about the framework. It is fun to work with, extremely flexible and provides a very specific set of tools for building modern apps. This post just focused on one use case and didn’t take into account the other initiatives such as integration with NativeScript for mobile apps. As a developer I don’t have too many complaints.

As a manager, however, I’ve got serious reservations. It feels like a complicated framework to learn and there is a lot of ritual and ceremony involved with building components that I don’t feel existing in the previous releases. I wonder how well this will scale and how easy it will be to ramp up team members on large projects and enable them to work effectively in parallel. It is early so many of the use cases and best practices haven’t emerged yet, and because Google is using Angular 2 on large projects of their own I’m confident many of these concerns will be addressed. In the end, it is still a product in beta but I have no doubt it will become the major framework of choice despite my reservations due to the incredible momentum Angular has gained over the past few years.

If you or your company are using Angular and would like to learn more about best practices and a path to migrate to Angular 2 or other frameworks, our team is available to help. We do everything from labs and training to custom workshops, project-based development, and shoulder-to-shoulder development with our experienced consultants working side by side with your team to share knowledge and maximize productivity. We have years of experience building Angular apps of all sizes. Visit our website to learn more then fill out our contact form and we will be back in touch!

I’d love to hear from you and learn about your own experience! Share your feedback, questions, concerns, and own experiences with Angular 2 using the comment form below.


4 comments to Angular 2.0: Opinions, Commentary, and a Migration Case Study

  1. Vishal
    February 4, 2016

    Nice article. The first question comes to me is why should we migrate to Angular 2.0 if my Angular 1.x application is running nice and smooth. Definitely as a developer I would be more happier to work with new standards and new syntax but what advantage a client/product owner get if we migrate to Angular 2. ?

    Reply
    • Jeremy Likness
      February 4, 2016

      The two main benefits that come to mind immediately are: (1) less friction – the sooner you migrate an app the easier it is. You either migrate with N components, or N + x components and that is always more effort. We try to stay on the leading edge of stable releases for that reason. But to your point, why even worry about the leading edge? That’s because of (2) performance – AngularJS 2.0 has demonstrated hands down it out performs almost all of the competing “older technology” libraries for browser rendering and refresh times, including the 1.x version. For data-intensive apps that is something to take very seriously. Otherwise, I agree with you … if there is a massive app that would incur a major cost from migration that is stable and meeting performance requirements, then there really is no compelling reason to migrate it. I would look at new projects or new components/modules in Ng2 and keep the Ng1 as is.

      Reply
      • Vishal
        February 5, 2016

        I agree with your opinion.
        I think migrating third party libraries should be a challenge as they would not be up to date with the latest standards. What do think about this ?

        Reply
        • Jeremy Likness
          February 5, 2016

          You can use the same third party libraries with Angular 2 that you used with Angular 1. You don’t need to migrate them … on the flipside, if their community does update them to keep up with latest standards, it’s fairly low friction provided the APIs don’t change.

          Reply

Leave a comment