Don’t overcomplicate your Angular migration
Reading time: 5 minutes
Are you currently working on a large, enterprise AngularJS application 😵and need to migrate to Angular? Not sure where to start? Scared you don’t have the resources? You’re not alone!
Migrations are tricky, and can pose lots of challenges since no two applications are the same 😪While migration can be stressful and seem like a black hole with endless problems and costs - it doesn’t have to be. Simplify your Angular migration by doing some planning up-front to prepare an appropriate migration strategy for you and your team. This will make things easier for you as it mitigates the risk of running into unforeseen problems and most importantly, will give you a blueprint on how to do it.
People often think that you need to do ‘the big bang approach’ and migrate all of the application at once. While this might make it easier in terms of setup and building, in some applications it’s not necessary nor is it the 'correct’ way of doing it. When deciding on a migration strategy, there are many areas to consider. To simplify things a bit, here are three of the big questions to consider:
- How do you handle routing?
- How do you handle state, and bridging of $rootScope and NgRx
- How do you approach migrating components?
While there is no single right answer, I will go over a few of the options you have available to help you plan out your migration strategy, and turn that black hole of unknown problems into actionable solutions.
How to handle Routing
Routing is a major part of Angular applications, and there are many questions to be answered:
- How do you handle routing in general?
- How do you make AngularJS handle route x and Angular handle route y?
- How do you handle resolving data for routes?
- How do you handle route protections such as an authentication guard?
The list is long 📄…
Migrating Route by Route
If you have a large application, and the migration effort will take awhile to complete, it might be worth considering running in a hybrid mode.With this approach, you migrate one route at a time and let Angular handle the migrated routes and AngularJS handle the non-migrated routes.This works best if the routes are fairly independent of each other and do not need to share state or communicate since you’re running two instances of your application on the same page.
Migrating One Big Route Containing Both AngularJS & Angular
If your application doesn’t have many routes, or if your routes contain complex screens with many components, migrating one route at a time might not be the best approach. This can pose the same challenges as the “big bang refactor” on a slightly smaller scale.
When not migrating an entire route at once, we need to migrate by component or module, and upgrade/downgrade them using the
When going with this approach, the first question you will need to answer is:
What version is going to control the routing?
AngularJS Controls the Routing
If AngularJS controls the routing, you would migrate your components to Angular, then use the
@angular/upgrade package to downgrade your Angular component into one that works with AngularJS
Angular Controls the Routing
If Angular is going to control the routing then you’ll need to use the @angular/upgrade package to upgrade all of your AngularJS components, and then over time, migrate them to Angular.
Handling AngularJS $rootScope and Angular NgRx
One question that might come up during a migration is how do you handle these centralized states? How do you sync these across the apps when you are migrating?It’s not unheard of in big applications to be leveraging the
$rootScope emitters and broadcastings to communicate across components and share state. In Angular we often use
NgRx or a similar Redux inspired library to handle and centralize state.
In my examples, I will assume you are using
Building a bridge between AngularJS and Angular might sound intimidating, but it’s actually quick and pain free. Let’s run through an example of adding items to a cart and see what it could look like:
In the AngularJS application, we are using
$rootScope to emit an event called
addCartItem, which includes the item we want to add to our cart.
In the Angular application using
NgRx there is an action called
AddCartItem that adds a cart item to the store.
To avoid an endless loop of AngularJS and Angular updating each other and triggering multiple API requests, we will add an event to AngularJS -
addCartItemFromAngular, and an NgRx action
In three easy steps we can build a bridge to get the two talking and sharing state:
Step 1 - access the
$rootScope from Angular
Sweet, two lines of code gave us access to the
Step 2 - Listen for any
AddCartItem emits on the
Cool, now we have the bridge from AngularJS → Angular, super simple.
Step 3 - Now we just need to do the opposite and listen for action dispatches, we can do so using the Effects. Make sure to set
dispatch: false, this will tell the Effect to not return anything but instead stop after this execution.
Upgrading and Downgrading Components
One thing to keep in mind when migrating from AngularJS to Angular is that you are able to downgrade/upgrade components, and it’s not very hard. Angular supplies you with a library 🙌 that you can use for upgrading/downgrading components, as well as services and directives.
The amazing thing is that you only need to update the parent component and not all sub-components. Whether it’s best to do a top-down or bottom-up approach depends on your specific case. If you quickly want to get the Angular app running without having to migrate every component then it would make sense to upgrade the parent component. However, if you are still running AngularJS then it would probably make sense to migrate the sub-components and downgrading them one by one.
Let’s say you have a library of custom wrapper components for buttons, inputs and you use them in a form, then if you upgrade that form, you wouldn’t need to upgrade the inputs/buttons being used by that form. This means that you only need to upgrade/downgrade the actual component you are using, any sub-component’s used in that said component do not need to be dealt with.
Imagine we have a route which contains a list of cart-items, that list renders each cart item using a sub-component which in turn uses sub-components for the layout. If we want to upgrade the list we would simply upgrade the list-component and all sub-components would work without any extra handling. Worth noting is that this is in a simple case where there is no state sharing or handlers running across multiple components/services. If you have a case where there are integrations running across components/services then you should consider any of the strategies I’ve mentioned above to handle that data.
Always plan ahead for your migration and choose a strategy to follow. Don’t be afraid of revisiting that chosen strategy and re-evaluate as you go. Try to map out any cross app dependencies first 📝to make sure you get any bridges/services working or at the very least planned and then move forward from there. ✅
A migration doesn’t need to be harder than what you make it! Don’t over complicate it. Go ahead and do your migration now, don’t wait! 🚀