Skip to main content

Overview

The Sushi Restaurant App uses Angular Router with Ionic’s routing enhancements to provide seamless navigation with lazy loading, route parameters, and preloading strategies.

Routing Architecture

1

Root Routing

App-level routes defined in app-routing.module.ts
2

Feature Routing

Feature-specific routes in home-routing.module.ts
3

Lazy Loading

Modules loaded on-demand for optimal performance
4

Route Parameters

Dynamic routes for detail views (:registroId)

Root Routing Module

The application-level routing configuration:
src/app/app-routing.module.ts
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () =>
      import('./home/home.module').then((m) => m.HomePageModule),
  },
  {
    path: '',
    redirectTo: 'home',
    pathMatch: 'full',
  },
];

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

Key Configuration

The forRoot() method configures the router at the application root level.
RouterModule.forRoot(routes, {
  preloadingStrategy: PreloadAllModules
})
Use forRoot() only once in the root module. Feature modules use forChild().
This strategy loads lazy modules in the background after the initial app load:Benefits:
  • Faster initial load time
  • Subsequent navigation is instant
  • Better user experience
How it works:
  1. Initial route loads first
  2. After app is stable, lazy modules load in background
  3. Modules are cached for instant access
The app loads the home page immediately, then preloads the detail page module
{
  path: '',
  redirectTo: 'home',
  pathMatch: 'full',
}
  • Empty path redirects to /home
  • pathMatch: 'full' ensures exact match
  • User navigating to / lands on /home

Lazy Loading Implementation

Dynamic Import Syntax

Lazy loading uses dynamic import() statements:
{
  path: 'home',
  loadChildren: () =>
    import('./home/home.module').then((m) => m.HomePageModule),
}
  1. Bundle Splitting: Angular creates separate JavaScript bundles for each lazy module
  2. On-Demand Loading: Module is downloaded only when the route is accessed
  3. Caching: Once loaded, the module remains in memory
  4. Code Splitting: Reduces initial bundle size significantly

Feature Routing: Home Module

Feature-level routing configuration:
src/app/home/home-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomePage } from './home.page';

const routes: Routes = [
  {
    path: '',
    component: HomePage,
  },
  {
    path: ':registroId',
    loadChildren: () =>
      import('./detalle-registro/detalle-registro.module').then(
        (m) => m.DetalleRegistroPageModule,
      ),
  },
];

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

Route Configuration Breakdown

{
  path: '',
  component: HomePage,
}
  • Renders HomePage at /home
  • Empty path is relative to parent route (home)
  • Eagerly loaded (component is in the same module)
Full URL: /home
{
  path: ':registroId',
  loadChildren: () =>
    import('./detalle-registro/detalle-registro.module').then(
      (m) => m.DetalleRegistroPageModule,
    ),
}
  • :registroId is a route parameter
  • Matches any value: /home/1, /home/2, etc.
  • Lazy loads detail page module
Example URLs:
  • /home/1 → Shows detail for item with ID “1”
  • /home/2 → Shows detail for item with ID “2”
The colon (:) prefix indicates a dynamic route parameter
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule],
})
  • Used in feature modules
  • Registers routes as children of parent routes
  • Can be imported multiple times (unlike forRoot())
Never use forRoot() in feature modules—it will create multiple router instances

Route Parameters

Extracting Route Parameters

Access dynamic route parameters in components:
src/app/home/detalle-registro/detalle-registro.page.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Registro } from '../home.model';
import { RegistrosServiceTs } from '../home.service';

@Component({
  standalone: false,
  selector: 'app-detalle-registro',
  templateUrl: './detalle-registro.page.html',
  styleUrls: ['./detalle-registro.page.scss'],
})
export class DetalleRegistroPage implements OnInit {
  registro: any = [];

  constructor(
    private activateRoute: ActivatedRoute,
    private registrosService: RegistrosServiceTs,
    private router: Router,
  ) {}

  ngOnInit() {
    // Subscribe to route parameter changes
    this.activateRoute.paramMap.subscribe((paramMap) => {
      let recipeId: string = String(paramMap.get('registroId'));
      this.registro = this.registrosService.getRegistro(recipeId);
      console.log(this.registro);
    });
  }
}

Parameter Extraction Methods

Programmatic Navigation

Using Router Service

Navigate programmatically from TypeScript code:
import { Router } from '@angular/router';

export class DetalleRegistroPage {
  constructor(private router: Router) {}

  async deleteRegistro() {
    // ... delete logic
    
    // Navigate back to home
    this.router.navigate(['/home']);
  }
}
// Navigate to absolute route
this.router.navigate(['/home']);
this.router.navigate(['/home', '1']);  // /home/1

Template Navigation

Navigate using routerLink directive in templates:
<!-- Absolute path -->
<ion-button [routerLink]="['/home']">Home</ion-button>

<!-- With parameter -->
<ion-button [routerLink]="['/home', registro.id]">View Details</ion-button>

<!-- String syntax -->
<ion-button routerLink="/home">Home</ion-button>

<!-- With active class -->
<ion-button 
  [routerLink]="['/home']" 
  routerLinkActive="active-link">
  Home
</ion-button>
routerLink is preferred over (click) handlers for navigation—it’s more semantic and handles accessibility better

Ionic Router Integration

IonicRouteStrategy

Ionic provides a custom route reuse strategy:
src/app/app.module.ts
import { RouteReuseStrategy } from '@angular/router';
import { IonicRouteStrategy } from '@ionic/angular';

@NgModule({
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
})
export class AppModule {}
Ionic’s custom strategy for managing route lifecycle:Features:
  • Preserves component state during navigation
  • Optimizes mobile navigation animations
  • Handles back button behavior
  • Manages component caching
Without IonicRouteStrategy, Angular’s default strategy would destroy components on every navigation
Example Scenario:
  1. User views list on home page
  2. User scrolls to item #10
  3. User taps item to view details
  4. User navigates back
  5. ✅ List is still scrolled to item #10 (state preserved)
Provides native-like behavior where back navigation restores previous state

ion-router-outlet

Ionic’s enhanced router outlet:
src/app/app.component.html
<ion-app>
  <ion-router-outlet></ion-router-outlet>
</ion-app>
Features:
  • Animated page transitions
  • Gesture-based navigation (swipe back)
  • Stack-based navigation (maintains navigation history)
  • Platform-specific animations (iOS vs Android)
ion-router-outlet extends Angular’s router-outlet with mobile-specific enhancements

Route Guards

While not implemented in the current app, route guards can protect routes:
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 {
    const isAuthenticated = // ... check auth status
    
    if (!isAuthenticated) {
      this.router.navigate(['/login']);
      return false;
    }
    return true;
  }
}

Available Guard Types

CanActivate

Determines if a route can be activated

CanDeactivate

Determines if user can leave a route (unsaved changes)

CanLoad

Prevents lazy loading of modules

Resolve

Pre-fetches data before activating route

Best Practices

// ✅ Good - Lazy loaded
{
  path: 'feature',
  loadChildren: () => import('./feature/feature.module').then(m => m.FeatureModule)
}

// ❌ Bad - Eagerly loaded
{
  path: 'feature',
  component: FeatureComponent
}
Lazy loading reduces initial bundle size and improves performance.
RouterModule.forRoot(routes, {
  preloadingStrategy: PreloadAllModules  // ✅ Enables preloading
})
Provides the best balance between initial load time and navigation speed.
// ✅ Good - Observable paramMap
this.route.paramMap.subscribe(params => {
  const id = params.get('id');
});

// ⚠️ Acceptable - Snapshot (if component is destroyed on navigation)
const id = this.route.snapshot.paramMap.get('id');

// ❌ Bad - Direct params access
const id = this.route.snapshot.params['id'];
// ✅ Good - Absolute path (clear and predictable)
this.router.navigate(['/home']);

// ⚠️ Use with caution - Relative path (depends on current location)
this.router.navigate(['../home'], { relativeTo: this.route });

Common Routing Patterns

Master-Detail Navigation

The app implements the master-detail pattern:
  1. Master (/home): List of all menu items
  2. Detail (/home/:id): Detailed view of single item
// Navigate from master to detail
<ion-card [routerLink]="[registro.id]" *ngFor="let registro of registros">
  <!-- Card content -->
</ion-card>

// Navigate from detail back to master
this.router.navigate(['/home']);

Nested Routes

The routing structure demonstrates nesting:
/home                    → HomePage (list)
/home/1                  → DetalleRegistroPage (detail for item 1)
/home/2                  → DetalleRegistroPage (detail for item 2)
Child routes inherit the parent path, creating a hierarchical URL structure

Next Steps

Architecture Overview

Learn about the application architecture patterns

Project Structure

Explore the directory layout and file organization

Build docs developers (and LLMs) love