There is a lot of discussion about going from AngularJS (Ng1) to Angular 2+ (NgX), however there is much less discussion about what to expect when going from NgX to Ng1. I made that technology change twice now between a few job and project switches I made in the last couple years. In this process I realized that I had taken many of the differences between the two for granted.
I decided I’d write about what differences in particular caught me off guard, since more and more teams are leaving AngularJS. While this sounds counterintuitive, I imagine many engineers are in my position of leaving Ng1 at some point in their career, and then returning to it after joining a new team or project like I did. Many of these projects still need developers with Ng1 experience to maintain legacy code or write new Ng1 products. Sometimes this happens just a couple months after their last Ng1 experience, but could also be far longer since their last exposure.
I will not be focusing on the technical details of Ng1 and its NgX successor, and will instead focus on the different developer experiences. Obviously there will be some technical detail thrown in.
I got into front end development using Ng1, and while I never fell in love with Ng1 I definitely became fond of the framework. It provided what I needed, and a simple enough learning curve to write clean and reusable code. I worked with it for a while and learned what parts to avoid and which worked better, for my own coding style and my team’s. I avoided things like using separate controllers for every directive in favor of linking things directly to the directive much of the time. I relied heavily on other aspects of the framework like the dependency injection and automatic change detection.
Then I switched projects and was using NgX, which, outside of observables, was an even simpler learning curve than Ng1 had been. Part of this was obviously due to my previous experience with Ng1, which does have some basic similarities. But observables in particular posed some challenge at first, primarily due to a misunderstanding I had about the difference between the observable sequence of execution and the observer of that sequence. But outside of that brief hiccup, NgX was beautiful. Components felt more than just self contained, but truly sovereign and in control of their own existence. The much better typed dependency injection system meant I could more readily avoid “props drilling”. This is when a value is passed down through through many components, a common problem I had with Ng1’s directive hierarchies. Things in NgX just felt...natural.
Then I moved back to Ng1.
To be honest, in the year or so I had become familiar with NgX I had forgotten about how often I was using asynchronous behavior. I had forgotten that Ng1 uses $$hashKey to track values for change detection during its digest cycle. I had forgotten why immutability had become so prominent a pattern in my coding patterns with NgX.
These issues came up in ways that, while an important (re)learning experience, were painful and felt unnecessary to still be dealing with on my return to Ng1. Ng1’s use of the $$hashKey property meant that whenever an object was created within Ng1’s context, I had to take care and remember what it was doing. I could no longer use Object.assign() or the spread operator to create new objects based on existing ones and call them immutable, as the $$hashKey property would be included unless explicitly handled or removed. Ng1 would still be tracking the object and updating it during the digest cycle. This played hell with my usage of Redux with Ng1 as it would cause unexpected and unwanted synchronization of changes.
More embarrassingly, issues implementing asynchronous behavior came up when I took for granted the safety zone.js provides in NgX. Namely, that zone.js guarantees that asynchronous code will only run once the function that made the async call completes, ensuring the expected execution path. While a great feature in NgX, I had not been thinking about this when I returned to Ng1 and wrote functions that would not complete due to calling an asynchronous function in the middle, as it would hijack my expected sequence of execution. I had remembered to handle async behavior properly in my React development experience, but had forgotten about this crucial difference in how async activities are handled between Ng1 and NgX.
My issues continued as I overused scope events. This is something I had specifically avoided doing during my first stint as an Ng1 developer. In NgX I had found it so easy to simply subscribe to an observable whenever I needed to react to events, but with Ng1 I then found it far harder to scale this approach with scope events. The result was convoluted and hard to debug as scope events work across an excessively broad area of the component hierarchy. Greatly exacerbating the issue, scope events are not provided to the directive or component using them, but can be emitted or acted upon from almost anywhere. In NgX observables are largely provided to the client component either as an input (e.g. EventEmitters) or via an injected dependency like a service.
All in all it was definitely a valuable set of lessons for me. In patience, remembering old practices and patterns, and in needing to spend more time re-familiarizing myself with my old tool set before proceeding. Hopefully this article highlights some issues that others may run into so that their immersion back into Ng1 goes more smoothly than the first month or so that mine did.
Share your own experiences relearning a technology in the comments!
The software industry moves fast. But if you keep up, you can have an incredible career.