how to build child routes based on service response

3 min read 04-10-2024
how to build child routes based on service response


Dynamically Building Child Routes in Angular Based on Service Response

In Angular, you often need to create child routes dynamically based on data retrieved from a service. This is particularly useful for scenarios where the structure of your application depends on user roles, backend configurations, or other factors that are determined at runtime. This article explores how to effectively build child routes based on service responses in your Angular application.

The Scenario: A Dynamic Menu

Imagine an e-commerce application where different user types (e.g., admins, customers, partners) have access to different functionalities. You want to display a dynamic menu based on the user's role, and each menu item should navigate to a specific child route within the application.

Original Code:

// app-routing.module.ts

const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    children: [
      // Define child routes here
    ]
  }
];

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

In this basic setup, child routes are defined statically. But, in our e-commerce example, we need a dynamic approach to build the child routes based on the user's role.

Dynamic Routing with Observables

To implement dynamic routing, we can leverage the power of Observables. The idea is to fetch user role data from a service and then use the data to dynamically build and register child routes. Here's a step-by-step breakdown:

  1. Define an Interface:

    // user-role.model.ts
    
    export interface UserRole {
      id: number;
      name: string;
      permissions: string[];
    }
    
  2. Create a User Service:

    // user.service.ts
    
    import { Injectable } from '@angular/core';
    import { Observable } from 'rxjs';
    import { UserRole } from './user-role.model';
    
    @Injectable({
      providedIn: 'root'
    })
    export class UserService {
      getUserRole(): Observable<UserRole> {
        // Implement logic to fetch user role from backend
        return of({ 
          id: 1, 
          name: 'admin', 
          permissions: ['manage_products', 'manage_orders'] 
        });
      }
    }
    
  3. Dynamic Route Creation:

    // app-routing.module.ts
    
    import { RouterModule, Routes, ActivatedRoute, Router } from '@angular/router';
    import { HomeComponent } from './home/home.component';
    import { UserService } from './user.service';
    import { Injectable, OnInit } from '@angular/core';
    import { map, switchMap } from 'rxjs/operators';
    
    @Injectable({
      providedIn: 'root'
    })
    export class DynamicRoutingService implements OnInit {
    
      constructor(
        private router: Router,
        private userService: UserService
      ) {}
    
      ngOnInit(): void {
        this.userService.getUserRole().pipe(
          map(userRole => {
            const childRoutes: Routes = [];
            if (userRole.permissions.includes('manage_products')) {
              childRoutes.push({ path: 'products', component: ProductsComponent });
            }
            if (userRole.permissions.includes('manage_orders')) {
              childRoutes.push({ path: 'orders', component: OrdersComponent });
            }
            return childRoutes;
          }),
          switchMap(childRoutes => {
            return this.router.navigateByUrl('/', {
              skipLocationChange: true,
              replaceUrl: true,
              queryParamsHandling: 'merge',
              state: { childRoutes }
            });
          })
        ).subscribe();
      }
    }
    
  4. Child Route Resolution:

    // app-routing.module.ts
    
    const routes: Routes = [
      {
        path: '',
        component: HomeComponent,
        children: [],
        resolve: {
          childRoutes: DynamicRoutingService
        }
      }
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    
  5. Updating Child Routes:

    // home/home.component.ts
    
    import { Component, OnInit } from '@angular/core';
    import { ActivatedRoute } from '@angular/router';
    
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    })
    export class HomeComponent implements OnInit {
    
      childRoutes: Routes = [];
    
      constructor(
        private route: ActivatedRoute
      ) {}
    
      ngOnInit(): void {
        this.route.data.subscribe(data => {
          this.childRoutes = data.childRoutes;
        });
      }
    }
    
  6. Displaying the Dynamic Menu:

    <!-- home/home.component.html -->
    
    <ul>
      <li *ngFor="let route of childRoutes">
        <a [routerLink]="[route.path]">{{route.path}}</a>
      </li>
    </ul>
    
    <router-outlet></router-outlet>
    

Conclusion

Building child routes dynamically based on service responses offers flexibility and scalability. By utilizing Observables and route resolvers, you can create user-specific navigation experiences and maintain a clean and organized routing structure. This approach empowers your Angular applications to adapt to various data-driven scenarios and deliver dynamic and personalized user interfaces.

Remember to handle error scenarios, use appropriate security measures, and thoroughly test your implementation to ensure a robust and reliable dynamic routing system.