When migrating an application from AngularJS (1.x) to Angular (2+), one of the biggest challenges is transitioning from UI-Router (@uirouter/angularjs
) to Angular Router (@angular/router
).
Since UI-Router and Angular Router work differently, developers often face issues like:
- Broken routes after migration
- State parameters not passing correctly
- Lazy loading issues
- Navigation problems due to different lifecycle hooks
This guide explains common UI-Router issues after migration and provides step-by-step solutions for each.
1. Key Differences Between UI-Router and Angular Router
Feature | UI-Router (@uirouter/angularjs ) | Angular Router (@angular/router ) |
---|---|---|
State-Based Navigation | Uses states ($stateProvider ) instead of routes | Uses route-based navigation (RouterModule ) |
Nested Views | Supports multiple views per state | Uses child routes with <router-outlet> |
URL Parameters | Uses :param or params object | Uses :param and queryParams |
Lazy Loading | Supports manual state lazy loading | Uses loadChildren |
Navigation Methods | $state.go('stateName') | router.navigate(['path']) |
2. Common Issues and Fixes
Issue #1: Routes Not Working After Migration
Problem
- After switching from UI-Router to Angular Router, some routes do not work, showing a blank page or
404 Not Found
.
Cause
- UI-Router follows state-based navigation (
$stateProvider
), while Angular Router uses path-based navigation (RouterModule
). - Old states may not match Angular Router’s path structure.
Solution: Convert UI-Router States to Angular Routes
Old UI-Router State
$stateProvider.state('home', {
url: '/home',
templateUrl: 'home.html',
controller: 'HomeController'
});
New Angular Route
const routes: Routes = [
{ path: 'home', component: HomeComponent }
];
- Add routes to
AppModule
:
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Issue #2: URL Parameters Not Passing Correctly
Problem
- UI-Router allows passing parameters as objects, but Angular Router requires them in the URL or queryParams.
- Example:
$state.go('product', { id: 123 })
works in UI-Router, butrouter.navigate(['/product'], { queryParams: { id: 123 } })
is required in Angular.
Solution: Update Navigation and Route Definitions
Old UI-Router
$stateProvider.state('product', {
url: '/product/:id',
templateUrl: 'product.html',
controller: 'ProductController'
});
jsCopyEdit$state.go('product', { id: 123 });
New Angular Router
const routes: Routes = [
{ path: 'product/:id', component: ProductComponent }
];
this.router.navigate(['/product', 123]);
Fetching Route Parameters in Angular
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.params.subscribe(params => {
console.log('Product ID:', params['id']);
});
}
Issue #3: Nested Views Not Rendering
Problem
- UI-Router supports multiple views using
views
inside a state. - Angular Router only supports one primary router outlet.
Solution: Use Child Routes in Angular Router
Old UI-Router Nested State
$stateProvider.state('dashboard', {
url: '/dashboard',
views: {
'sidebar': { templateUrl: 'sidebar.html' },
'content': { templateUrl: 'dashboard.html' }
}
});
New Angular Router with Child Routes
const routes: Routes = [
{
path: 'dashboard', component: DashboardComponent,
children: [
{ path: 'sidebar', component: SidebarComponent },
{ path: 'content', component: DashboardContentComponent }
]
}
];
Updated HTML
<router-outlet></router-outlet>
<router-outlet name="sidebar"></router-outlet>
Issue #4: Lazy Loading Not Working
Problem
- UI-Router uses
lazyLoad
inside state definitions, while Angular Router usesloadChildren
.
Solution: Convert UI-Router Lazy Loading to Angular
Old UI-Router Lazy Loading
$stateProvider.state('lazy', {
url: '/lazy',
lazyLoad: function(transition) {
return import('./lazy.module').then(mod => {
return mod.LazyModule;
});
}
});
New Angular Lazy Loading
const routes: Routes = [
{ path: 'lazy', loadChildren: () => import('./lazy.module').then(m => m.LazyModule) }
];
Ensure LazyModule
has its own RouterModule
:
@NgModule({
imports: [RouterModule.forChild([{ path: '', component: LazyComponent }])],
exports: [RouterModule]
})
export class LazyModule {}
Issue #5: $state.go()
Not Working
Problem
$state.go()
from UI-Router no longer works after migrating to Angular.
Solution: Replace $state.go()
with router.navigate()
Old UI-Router
$scope.goToDashboard = function() {
$state.go('dashboard');
};
New Angular Router
constructor(private router: Router) {}
goToDashboard() {
this.router.navigate(['/dashboard']);
}
Issue #6: UI-Router $transition$
Hooks Not Triggering
Problem
- UI-Router allows transition hooks like:
$transitions.onStart({ to: '*' }, function(transition) {
console.log('Navigation started:', transition.to().name);
});
- Angular Router does not support hooks like UI-Router.
Solution: Use Angular Router Events
import { Router, NavigationStart } from '@angular/router';
constructor(private router: Router) {}
ngOnInit() {
this.router.events.subscribe(event => {
if (event instanceof NavigationStart) {
console.log('Navigation started:', event.url);
}
});
}
Best Practices for Migration
✔ Convert UI-Router states to Angular Router routes one at a time.
✔ Use Angular’s ActivatedRoute
instead of $stateParams
.
✔ Ensure nested views are properly converted into child routes.
✔ Use loadChildren
instead of lazyLoad
.
✔ Replace $state.go()
with router.navigate()
.
✔ Monitor navigation events using Router
events.