Chapter 06 - AngularJS to Angular: Mastering the Migration for Modern Web Development

AngularJS and Angular differ significantly. Angular uses TypeScript, component-based architecture, and unidirectional data flow. Migration involves gradual component transformation, using ngUpgrade for hybrid apps, and updating services and routing.

Chapter 06 - AngularJS to Angular: Mastering the Migration for Modern Web Development

AngularJS and Angular might sound similar, but they’re actually quite different beasts. I remember when I first started working with AngularJS back in the day. It was a game-changer for building dynamic web apps. But as web development evolved, so did Angular.

The shift from AngularJS to Angular was pretty significant. AngularJS, also known as Angular 1.x, was built on JavaScript and used a Model-View-Controller (MVC) architecture. Angular, on the other hand, is a complete rewrite using TypeScript and follows a component-based architecture.

One of the biggest differences I noticed was how Angular handles data binding. In AngularJS, we had two-way data binding by default, which was cool but could sometimes lead to performance issues in complex apps. Angular introduced a unidirectional data flow, which gives us more control over how data moves through our application.

Another major change was the introduction of modules and components in Angular. These concepts make it easier to organize and structure our code. I found this particularly helpful when working on larger projects with multiple team members.

The dependency injection system also got a major overhaul in Angular. It’s more powerful and flexible now, which makes testing and maintaining our code a lot easier.

But let’s talk about the elephant in the room - migrating from AngularJS to Angular. It’s not a walk in the park, I’ll tell you that. But it’s definitely doable, and the benefits are worth it in the long run.

The Angular team, bless their hearts, created a tool called ngUpgrade to help us with this migration process. It allows us to run both AngularJS and Angular in the same application, which is pretty neat. This means we can migrate our app piece by piece, rather than having to do a complete rewrite all at once.

So, let’s dive into the migration process. First things first, we need to prepare our AngularJS app for the upgrade. This involves cleaning up our code and making sure we’re following best practices. Trust me, this step will save you a lot of headaches down the line.

One of the key things to do is to start using components in your AngularJS app. Components were introduced in AngularJS 1.5, and they’re very similar to Angular components. This will make the transition smoother.

Here’s an example of how you might refactor an AngularJS directive into a component:

// Before: AngularJS directive
angular.module('myApp').directive('myDirective', function() {
  return {
    restrict: 'E',
    template: '<div>{{ctrl.message}}</div>',
    controller: function() {
      this.message = 'Hello, World!';
    },
    controllerAs: 'ctrl'
  };
});

// After: AngularJS component
angular.module('myApp').component('myComponent', {
  template: '<div>{{$ctrl.message}}</div>',
  controller: function() {
    this.message = 'Hello, World!';
  }
});

Once we’ve cleaned up our AngularJS code, it’s time to set up the hybrid application. This is where ngUpgrade comes in. We’ll install Angular and the upgrade module in our existing AngularJS project.

npm install @angular/upgrade @angular/core @angular/common @angular/compiler @angular/platform-browser @angular/platform-browser-dynamic

Next, we need to bootstrap our hybrid app. This involves creating a new Angular module and using the UpgradeModule to create a hybrid app:

import { NgModule } from '@angular/core';
import { UpgradeModule } from '@angular/upgrade/static';

@NgModule({
  imports: [
    UpgradeModule
  ]
})
export class AppModule {
  constructor(private upgrade: UpgradeModule) { }
  ngDoBootstrap() {
    this.upgrade.bootstrap(document.body, ['myApp'], { strictDi: true });
  }
}

Now comes the fun part - migrating our components one by one. We can start with the leaf components (those that don’t depend on other components) and work our way up.

For each component, we’ll create a new Angular component and use the downgradeComponent function to make it available in AngularJS:

import { Component } from '@angular/core';
import { downgradeComponent } from '@angular/upgrade/static';

@Component({
  selector: 'my-new-component',
  template: '<div>{{message}}</div>'
})
export class MyNewComponent {
  message = 'Hello from Angular!';
}

angular.module('myApp').directive(
  'myNewComponent',
  downgradeComponent({ component: MyNewComponent }) as angular.IDirectiveFactory
);

We can also go the other way and use AngularJS components in our Angular code using the upgradeComponent function:

import { Directive, ElementRef, Injector } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';

@Directive({
  selector: 'my-ng1-component'
})
export class MyNg1ComponentDirective extends UpgradeComponent {
  constructor(elementRef: ElementRef, injector: Injector) {
    super('myNg1Component', elementRef, injector);
  }
}

As we migrate more and more components, we’ll gradually shift our application from AngularJS to Angular. It’s like watching a caterpillar turn into a butterfly, except with more TypeScript and fewer wings.

One thing to keep in mind during this process is services. In AngularJS, we often used services to share data and functionality across components. In Angular, we can achieve similar results with injectable services:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class MyService {
  getData() {
    return 'Some data from Angular service';
  }
}

We can then use the downgradeInjectable function to make this service available in our AngularJS code:

import { downgradeInjectable } from '@angular/upgrade/static';
import { MyService } from './my.service';

angular.module('myApp').factory('myService', downgradeInjectable(MyService));

As we progress through the migration, we’ll also need to update our routing. Angular uses its own router, which is quite different from the AngularJS UI-Router or ngRoute. This might involve rewriting our routes and updating how we handle navigation in our app.

Here’s a simple example of how routing looks in Angular:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: '', redirectTo: '/home', pathMatch: 'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Throughout this process, it’s crucial to keep testing our application. We want to make sure we’re not breaking anything as we migrate. This is where having a good set of unit tests comes in handy. If you didn’t have tests before, now’s a great time to start adding them!

Once we’ve migrated all our components, services, and routes to Angular, we can finally remove the AngularJS code and dependencies. It’s like saying goodbye to an old friend, but sometimes we need to move on to bigger and better things.

The last step is to do a final cleanup and optimization of our Angular code. This might involve refactoring some components, improving our use of RxJS for handling asynchronous operations, or implementing lazy loading for better performance.

Migrating from AngularJS to Angular is definitely a journey. It requires patience, planning, and a fair bit of elbow grease. But in the end, you’ll have a modern, efficient, and more maintainable application. Plus, you’ll have learned a ton in the process.

Remember, every application is different, so your migration path might not look exactly like this. The key is to take it step by step, test frequently, and not be afraid to ask for help when you need it. There’s a great community of Angular developers out there who’ve been through this process and are usually happy to lend a hand.

So, are you ready to take your AngularJS app to the next level? Grab a cup of coffee (or your beverage of choice), roll up your sleeves, and let’s start migrating!