angular-migration

CategoryGeneral
AuthorAgentic Awesome Skills 社区
LicenseMIT
Rating4.50/5
Uses15.3K

Angular Migration

Master AngularJS to Angular migration, including hybrid apps, component conversion, dependency injection changes, and routing migration.

Use this skill when

  • Migrating AngularJS (1.x) applications to Angular (2+)
  • Running hybrid AngularJS/Angular applications
  • Converting directives to components
  • Modernizing dependency injection
  • Migrating routing systems
  • Updating to latest Angular versions
  • Implementing Angular best practices

Do not use this skill when

  • You are not migrating from AngularJS to Angular
  • The app is already on a modern Angular version
  • You need only a small UI fix without framework changes

Instructions

1. Assess the AngularJS codebase, dependencies, and migration risks.
2. Choose a migration strategy (hybrid vs rewrite) and define milestones.
3. Set up ngUpgrade and migrate modules, components, and routing.
4. Validate with tests and plan a safe cutover.

Safety

  • Avoid big-bang cutovers without rollback and staging validation.
  • Keep hybrid compatibility testing during incremental migration.

Migration Strategies

1. Big Bang (Complete Rewrite)

  • Rewrite entire app in Angular
  • Parallel development
  • Switch over at once
  • Best for: Small apps, green field projects

2. Incremental (Hybrid Approach)

  • Run AngularJS and Angular side-by-side
  • Migrate feature by feature
  • ngUpgrade for interop
  • Best for: Large apps, continuous delivery

3. Vertical Slice

  • Migrate one feature completely
  • New features in Angular, maintain old in AngularJS
  • Gradually replace
  • Best for: Medium apps, distinct features

Hybrid App Setup

typescript
// main.ts - Bootstrap hybrid app
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { UpgradeModule } from '@angular/upgrade/static';
import { AppModule } from './app/app.module';

platformBrowserDynamic()
.bootstrapModule(AppModule)
.then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule);
// Bootstrap AngularJS
upgrade.bootstrap(document.body, ['myAngularJSApp'], { strictDi: true });
});

typescript
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static';

@NgModule({
imports: [
BrowserModule,
UpgradeModule
]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) {}

ngDoBootstrap() {
// Bootstrapped manually in main.ts
}
}

Component Migration

AngularJS Controller → Angular Component

javascript
// Before: AngularJS controller
angular.module('myApp').controller('UserController', function($scope, UserService) {
  $scope.user = {};

$scope.loadUser = function(id) {
UserService.getUser(id).then(function(user) {
$scope.user = user;
});
};

$scope.saveUser = function() {
UserService.saveUser($scope.user);
};
});

typescript
// After: Angular component
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';

@Component({
selector: 'app-user',
template:
<div>
<h2>{{ user.name }}</h2>
<button (click)="saveUser()">Save</button>
</div>

})
export class UserComponent implements OnInit {
user: any = {};

constructor(private userService: UserService) {}

ngOnInit() {
this.loadUser(1);
}

loadUser(id: number) {
this.userService.getUser(id).subscribe(user => {
this.user = user;
});
}

saveUser() {
this.userService.saveUser(this.user);
}
}

AngularJS Directive → Angular Component

javascript
// Before: AngularJS directive
angular.module('myApp').directive('userCard', function() {
  return {
    restrict: 'E',
    scope: {
      user: '=',
      onDelete: '&'
    },
    template: 
      <div class="card">
        <h3>{{ user.name }}</h3>
        <button ng-click="onDelete()">Delete</button>
      </div>
    
  };
});
typescript
// After: Angular component
import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
selector: 'app-user-card',
template:
<div class="card">
<h3>{{ user.name }}</h3>
<button (click)="delete.emit()">Delete</button>
</div>

})
export class UserCardComponent {
@Input() user: any;
@Output() delete = new EventEmitter<void>();
}

// Usage: <app-user-card [user]="user" (delete)="handleDelete()"></app-user-card>

Service Migration

javascript
// Before: AngularJS service
angular.module('myApp').factory('UserService', function($http) {
  return {
    getUser: function(id) {
      return $http.get('/api/users/' + id);
    },
    saveUser: function(user) {
      return $http.post('/api/users', user);
    }
  };
});
typescript
// After: Angular service
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) {}

getUser(id: number): Observable<any> {
return this.http.get(/api/users/${id});
}

saveUser(user: any): Observable<any> {
return this.http.post('/api/users', user);
}
}

Dependency Injection Changes

Downgrading Angular → AngularJS

typescript
// Angular service
import { Injectable } from '@angular/core';

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

// Make available to AngularJS
import { downgradeInjectable } from '@angular/upgrade/static';

angular.module('myApp')
.factory('newService', downgradeInjectable(NewService));

// Use in AngularJS
angular.module('myApp').controller('OldController', function(newService) {
console.log(newService.getData());
});

Upgrading AngularJS → Angular

typescript
// AngularJS service
angular.module('myApp').factory('oldService', function() {
  return {
    getData: function() {
      return 'data from AngularJS';
    }
  };
});

// Make available to Angular
import { InjectionToken } from '@angular/core';

export const OLD_SERVICE = new InjectionToken<any>('oldService');

@NgModule({
providers: [
{
provide: OLD_SERVICE,
useFactory: (i: any) => i.get('oldService'),
deps: ['$injector']
}
]
})

// Use in Angular
@Component({...})
export class NewComponent {
constructor(@Inject(OLD_SERVICE) private oldService: any) {
console.log(this.oldService.getData());
}
}

Routing Migration

javascript
// Before: AngularJS routing
angular.module('myApp').config(function($routeProvider) {
  $routeProvider
    .when('/users', {
      template: '<user-list></user-list>'
    })
    .when('/users/:id', {
      template: '<user-detail></user-detail>'
    });
});
typescript
// After: Angular routing
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
{ path: 'users', component: UserListComponent },
{ path: 'users/:id', component: UserDetailComponent }
];

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

Forms Migration

html
<!-- Before: AngularJS -->
<form name="userForm" ng-submit="saveUser()">
  <input type="text" ng-model="user.name" required>
  <input type="email" ng-model="user.email" required>
  <button ng-disabled="userForm.$invalid">Save</button>
</form>
typescript
// After: Angular (Template-driven)
@Component({
  template: 
    <form #userForm="ngForm" (ngSubmit)="saveUser()">
      <input type="text" [(ngModel)]="user.name" name="name" required>
      <input type="email" [(ngModel)]="user.email" name="email" required>
      <button [disabled]="userForm.invalid">Save</button>
    </form>
  
})

// Or Reactive Forms (preferred)
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
template:
<form [formGroup]="userForm" (ngSubmit)="saveUser()">
<input formControlName="name">
<input formControlName="email">
<button [disabled]="userForm.invalid">Save</button>
</form>

})
export class UserFormComponent {
userForm: FormGroup;

constructor(private fb: FormBuilder) {
this.userForm = this.fb.group({
name: ['', Validators.required],
email: ['', [Validators.required, Validators.email]]
});
}

saveUser() {
console.log(this.userForm.value);
}
}

Migration Timeline

code
Phase 1: Setup (1-2 weeks)
  • Install Angular CLI
  • Set up hybrid app
  • Configure build tools
  • Set up testing

Phase 2: Infrastructure (2-4 weeks)

  • Migrate services

  • Migrate utilities

  • Set up routing

  • Migrate shared components

Phase 3: Feature Migration (varies)

  • Migrate feature by feature

  • Test thoroughly

  • Deploy incrementally

Phase 4: Cleanup (1-2 weeks)

  • Remove AngularJS code

  • Remove ngUpgrade

  • Optimize bundle

  • Final testing

Resources

  • references/hybrid-mode.md: Hybrid app patterns
  • references/component-migration.md: Component conversion guide
  • references/dependency-injection.md: DI migration strategies
  • references/routing.md: Routing migration
  • assets/hybrid-bootstrap.ts: Hybrid app template
  • assets/migration-timeline.md: Project planning
  • scripts/analyze-angular-app.sh: App analysis script

Best Practices

1. Start with Services: Migrate services first (easier)
2. Incremental Approach: Feature-by-feature migration
3. Test Continuously: Test at every step
4. Use TypeScript: Migrate to TypeScript early
5. Follow Style Guide: Angular style guide from day 1
6. Optimize Later: Get it working, then optimize
7. Document: Keep migration notes

Common Pitfalls

  • Not setting up hybrid app correctly
  • Migrating UI before logic
  • Ignoring change detection differences
  • Not handling scope properly
  • Mixing patterns (AngularJS + Angular)
  • Inadequate testing

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.
Join our Telegram