14
Module 14: Routing Between Angular Micro Frontends
Chapter 14 • Advanced
30 min
Routing Between Angular Micro Frontends
Users navigate normally:
/products/checkout
Behind the scenes:
- Routes load different applications
- Everything feels like a single SPA
Routing Architecture
Host Routing
typescript.js
const routes: Routes = [
{
path: 'products',
loadChildren: () => import('products/ProductsModule')
.then(m => m.ProductsModule)
},
{
path: 'checkout',
loadChildren: () => import('checkout/CheckoutModule')
.then(m => m.CheckoutModule)
},
{
path: '',
redirectTo: '/products',
pathMatch: 'full'
}
]
How It Works
- User navigates to
/products - Host detects route
- Host loads Products remote module
- Products module handles its own routes
- User sees Products app
Remote Routing
Products Remote Routes
typescript.js
// products-app/src/app/products/products-routing.module.ts
const routes: Routes = [
{ path: '', component: ProductsListComponent },
{ path: ':id', component: ProductDetailComponent }
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ProductsRoutingModule {}
Checkout Remote Routes
typescript.js
// checkout-app/src/app/checkout/checkout-routing.module.ts
const routes: Routes = [
{ path: '', component: CartComponent },
{ path: 'payment', component: PaymentComponent },
{ path: 'confirmation', component: ConfirmationComponent }
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CheckoutRoutingModule {}
Route Configuration
Host App Routing Module
typescript.js
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
const routes: Routes = [
{
path: 'products',
loadChildren: () => import('products/ProductsModule')
.then(m => m.ProductsModule)
},
{
path: 'checkout',
loadChildren: () => import('checkout/CheckoutModule')
.then(m => m.CheckoutModule)
}
]
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Navigation Patterns
Programmatic Navigation
typescript.js
import { Router } from '@angular/router'
export class ProductsComponent {
constructor(private router: Router) {}
goToCheckout() {
this.router.navigate(['/checkout'])
}
goToProduct(id: string) {
this.router.navigate(['/products', id])
}
}
Template Navigation
html.js
<a routerLink="/products">Products</a>
<a routerLink="/checkout">Checkout</a>
<a [routerLink]="['/products', productId]">View Product</a>
Cross-App Navigation
From Products to Checkout
typescript.js
// In Products app
export class ProductDetailComponent {
constructor(private router: Router) {}
addToCart() {
// Navigate to checkout (different app)
this.router.navigate(['/checkout'])
}
}
How it works:
- Products app uses shared Router
- Router is provided by Host
- Navigation works across apps
Route Guards
Auth Guard (Host Level)
typescript.js
import { Injectable } from '@angular/core'
import { CanActivate, Router } from '@angular/router'
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private router: Router) {}
canActivate(): boolean {
if (this.isAuthenticated()) {
return true
}
this.router.navigate(['/login'])
return false
}
private isAuthenticated(): boolean {
// Check authentication
return true
}
}
Use in Routes
typescript.js
const routes: Routes = [
{
path: 'checkout',
loadChildren: () => import('checkout/CheckoutModule')
.then(m => m.CheckoutModule),
canActivate: [AuthGuard]
}
]
Route Data Sharing
Passing Data via Route
typescript.js
// Navigate with state
this.router.navigate(['/checkout'], {
state: { cartItems: this.cartItems }
})
// Read state
const navigation = this.router.getCurrentNavigation()
const state = navigation?.extras.state
Query Parameters
typescript.js
// Navigate with query params
this.router.navigate(['/products'], {
queryParams: { category: 'electronics' }
})
// Read query params
this.route.queryParams.subscribe(params => {
const category = params['category']
})
Deep Linking
Support Deep Links
typescript.js
// Direct URL: /products/123
// Works automatically with Angular routing
const routes: Routes = [
{
path: 'products',
loadChildren: () => import('products/ProductsModule')
.then(m => m.ProductsModule)
}
]
// Products module handles: /products/:id
Route Preloading
Preload Strategy
typescript.js
import { PreloadAllModules } from '@angular/router'
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})
Benefits:
- ✅ Faster navigation
- ✅ Better UX
- ✅ Background loading
Error Handling
Handle Route Errors
typescript.js
{
path: 'products',
loadChildren: () => import('products/ProductsModule')
.then(m => m.ProductsModule)
.catch(err => {
console.error('Failed to load products module', err)
// Fallback route
return import('./fallback/fallback.module')
.then(m => m.FallbackModule)
})
}
Standalone Routing
Remote Apps Work Standalone
typescript.js
// products-app can run standalone
const routes: Routes = [
{
path: 'products',
loadChildren: () => import('./products/products.module')
.then(m => m.ProductsModule)
}
]
Benefits:
- ✅ Development without Host
- ✅ Testing in isolation
- ✅ Independent deployment
Best Practices
✅ Do
- Use consistent route structure
- Handle route errors
- Support deep linking
- Use route guards
- Preload critical routes
❌ Don't
- Don't hardcode routes
- Don't ignore errors
- Don't break deep links
- Don't skip route guards
Next Steps
Routing is configured!
Next module: Deployment strategies.