angular-migration
Master AngularJS to Angular migration, including hybrid apps, component conversion, dependency injection changes, and routing migration.
- risk
- unknown
- source
- community
- date added
- 2026-02-27
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
- Assess the AngularJS codebase, dependencies, and migration risks.
- Choose a migration strategy (hybrid vs rewrite) and define milestones.
- Set up ngUpgrade and migrate modules, components, and routing.
- 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
// 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 }); });
// 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
// 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); }; });
// 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
// 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> ` }; });
// 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
// 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); } }; });
// 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
// 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
// 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
// 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>' }); });
// 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
<!-- 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>
// 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
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
- Start with Services: Migrate services first (easier)
- Incremental Approach: Feature-by-feature migration
- Test Continuously: Test at every step
- Use TypeScript: Migrate to TypeScript early
- Follow Style Guide: Angular style guide from day 1
- Optimize Later: Get it working, then optimize
- 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