Skip to main content

Routing Strategy

Happy Habitat uses HashLocationStrategy for routing, which prefixes all URLs with #:
// app.config.ts
{ provide: LocationStrategy, useClass: HashLocationStrategy }
Example URLs:
  • http://localhost:4200/#/home
  • http://localhost:4200/#/auth/login
  • http://localhost:4200/#/resident/visitantes
Why Hash Strategy?
  • No server-side configuration required
  • Works with any hosting environment
  • Compatible with static file servers

Root Routes

Location: src/app/app.routes.ts

Route Structure

export const routes: Routes = [
  // Root redirect
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  
  // Public authentication routes
  { path: 'auth', loadChildren: () => import('./auth/auth-routes') },
  
  // Protected routes
  { path: 'home', loadComponent: ..., canActivate: [authGuard] },
  { path: 'dashboard', loadComponent: ..., canActivate: [authGuard] },
  
  // Role-based routes
  { path: 'sysadmin', loadChildren: ..., canActivate: [roleGuard(...)] },
  { path: 'admincompany', loadChildren: ..., canActivate: [roleGuard(...)] },
  { path: 'vigilancia', loadChildren: ..., canActivate: [roleGuard(...)] },
  { path: 'resident', loadChildren: ..., canActivate: [authGuard] },
  
  // 404 fallback
  { path: '**', component: NotFoundPageComponent }
];

Public Routes

Authentication Routes

Path: /auth/*
// auth/auth-routes.ts
export const authRoutes: Routes = [
  {
    path: '',
    component: AuthPageComponent,  // Layout without header/menu/footer
    children: [
      { path: 'login', component: LoginComponent },
      { path: 'register', component: RegisterComponent },
      { path: 'forgot-password', component: ForgotPasswordComponent },
      { path: 'reset-password', component: ResetPasswordComponent, canActivate: [authGuard] },
      { path: '', redirectTo: 'login', pathMatch: 'full' }
    ]
  }
];
Available Routes:
  • /auth/login - User login
  • /auth/register - New user registration
  • /auth/forgot-password - Password recovery
  • /auth/reset-password - Password reset (requires auth)
Layout: Uses AuthPageComponent which provides a clean layout without application chrome.

Protected Routes

Routes that require authentication use the authGuard.

General Routes

RouteComponentGuardDescription
/homeHomePageComponentauthGuardLanding page
/dashboardDashboardPageComponentauthGuardDashboard
/comunicadosComunicadosListComponentauthGuardAnnouncements
/proveedoresProveedoresServiciosComponentauthGuardService providers
/anunciosComunicadosListComponentauthGuardPublic notices
/socialPostsListComponentauthGuardSocial feed
/documentsDocumentsPageComponentauthGuardDocuments

Feature Routes (Lazy Loaded)

Amenities: /amenidades/*
{
  path: 'amenidades',
  loadChildren: () => import('./components/amenidades/amenidades-routes'),
  canActivate: [authGuard]
}
See: Amenity Routes Resident Portal: /resident/*
{
  path: 'resident',
  loadChildren: () => import('./components/resident/resident-routes'),
  canActivate: [authGuard]
}
See: Resident Routes

Role-Based Routes

Routes that require specific user roles use roleGuard.

System Administrator Routes

Path: /sysadmin/*
{
  path: 'sysadmin',
  loadChildren: () => import('./components/system-administation/system-admin-routes'),
  canActivate: [roleGuard([RolesEnum.SYSTEM_ADMIN, RolesEnum.ADMIN_COMPANY])]
}
Required Roles: SYSTEM_ADMIN or ADMIN_COMPANY

Admin Company Routes

Path: /admincompany/*
{
  path: 'admincompany',
  loadChildren: () => import('./components/admincompany/admincompany-routes'),
  canActivate: [roleGuard([RolesEnum.ADMIN_COMPANY])]
}
Required Role: ADMIN_COMPANY Available Routes:
  • /admincompany/morosos - Delinquent accounts
  • /admincompany/saldo-banco - Bank balance
  • /admincompany/historial-pagos-residente - Payment history

Vigilance Routes

Path: /vigilancia/*
{
  path: 'vigilancia',
  loadChildren: () => import('./components/vigilancia/vigilancia-routes'),
  canActivate: [roleGuard([RolesEnum.VIGILANCE])]
}
Required Role: VIGILANCE Available Routes:
  • /vigilancia/incidentes - Incident tracking
  • /vigilancia/reservaciones - Reservation management
  • /vigilancia/residentes - Resident monitoring
  • /vigilancia/tickets - Support tickets

Feature Route Details

Resident Routes

Location: src/app/components/resident/resident-routes.ts
export const residentRoutes: Routes = [
  {
    path: '',
    component: ResidentActividadesLayoutComponent,  // Resident layout
    children: [
      { path: 'visitantes', component: RegistroVisitanteComponent },
      { path: 'preferencias', component: RegistroPreferenciasComponent },
      { path: 'mascotas', component: RegistroMascotaComponent },
      { path: 'autos', component: RegistroAutoComponent },
      { path: 'reservaciones', component: ReservacionesComponent },
      { path: 'encuestas', component: ResidentEncuestasComponent },
      { path: 'encuestas/:id', component: ResidentEncuestaResponderComponent },
      { path: 'tickets', component: TicketsComponent },
      { path: 'tickets/nuevo', component: TicketFormComponent },
      { path: 'tickets/:id', component: TicketDetailComponent },
      { path: 'pagos', component: ResidentPagosListComponent },
      { path: 'pagos/nuevo', component: ResidentPagoFormComponent },
      { path: 'pagos/:id', component: ResidentPagoDetailComponent },
      { path: '**', redirectTo: 'visitantes' }
    ]
  }
];
Route Patterns:
PatternExampleDescription
/resident/visitantes-Visitor registration
/resident/preferencias-User preferences
/resident/mascotas-Pet registration
/resident/autos-Vehicle registration
/resident/reservaciones-Amenity reservations
/resident/encuestas-Available surveys
/resident/encuestas/:id/resident/encuestas/123Take specific survey
/resident/tickets-Support tickets list
/resident/tickets/nuevo-Create new ticket
/resident/tickets/:id/resident/tickets/456Ticket detail
/resident/pagos-Payment history
/resident/pagos/nuevo-New payment
/resident/pagos/:id/resident/pagos/789Payment detail
Layout: Uses ResidentActividadesLayoutComponent for consistent resident portal UI.

Amenity Routes

Location: src/app/components/amenidades/amenidades-routes.ts Available Routes:
  • /amenidades - Amenity list/grid
  • /amenidades/:id - Amenity detail
  • /amenidades/reservar/:id - Make reservation

Authentication Guard

Location: src/app/guards/auth.guard.ts
export const authGuard: CanActivateFn = (route, state) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.checkAuth()) {
    return true;
  }

  // Redirect to login with return URL
  router.navigate(['/auth/login'], { 
    queryParams: { returnUrl: state.url } 
  });
  return false;
};
Purpose: Verify user authentication before accessing protected routes. Behavior:
  • Checks if user is authenticated via AuthService
  • Allows access if authenticated
  • Redirects to /auth/login with return URL if not authenticated
Return URL: After login, user is redirected back to the originally requested URL.

Role Guard

Location: src/app/guards/role.guard.ts
export const roleGuard = (allowedRoles: string[]): CanActivateFn => {
  return (route, state) => {
    const authService = inject(AuthService);
    const router = inject(Router);

    // First check authentication
    if (!authService.checkAuth()) {
      router.navigate(['/auth/login'], { 
        queryParams: { returnUrl: state.url } 
      });
      return false;
    }

    // Then check roles
    if (authService.hasAnyRole(allowedRoles)) {
      return true;
    }

    // Insufficient permissions - redirect to home
    router.navigate(['/home']);
    return false;
  };
};
Purpose: Enforce role-based access control. Parameters:
  • allowedRoles: string[] - List of roles that can access the route
Behavior:
  1. Checks authentication first
  2. Verifies user has at least one of the allowed roles
  3. Redirects to /home if user lacks required role
Usage Example:
{
  path: 'sysadmin',
  loadChildren: ...,
  canActivate: [roleGuard([RolesEnum.SYSTEM_ADMIN, RolesEnum.ADMIN_COMPANY])]
}

Route Parameters

Reading Route Parameters

import { ActivatedRoute } from '@angular/router';

export class DetailComponent {
  constructor(private route: ActivatedRoute) {
    const id = this.route.snapshot.paramMap.get('id');
    // Or with Observable
    this.route.paramMap.subscribe(params => {
      const id = params.get('id');
    });
  }
}

Query Parameters

Setting Query Params:
this.router.navigate(['/auth/login'], {
  queryParams: { returnUrl: '/dashboard' }
});
Reading Query Params:
this.route.queryParamMap.subscribe(params => {
  const returnUrl = params.get('returnUrl');
});

Programmatic Navigation

Using Router Service

import { Router } from '@angular/router';

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

  navigateToHome() {
    this.router.navigate(['/home']);
  }

  navigateToDetail(id: string) {
    this.router.navigate(['/resident/tickets', id]);
  }

  navigateWithQueryParams() {
    this.router.navigate(['/search'], {
      queryParams: { q: 'amenities', filter: 'active' }
    });
  }
}
<!-- Simple navigation -->
<a routerLink="/home">Home</a>

<!-- With parameters -->
<a [routerLink]="['/resident/tickets', ticketId]">View Ticket</a>

<!-- With query params -->
<a [routerLink]="['/search']" [queryParams]="{q: 'test'}">Search</a>

<!-- Active link styling -->
<a routerLink="/home" routerLinkActive="active-link">Home</a>

Lazy Loading

All feature routes use lazy loading for optimal bundle size:
{
  path: 'resident',
  loadChildren: () => import('./components/resident/resident-routes')
}
Benefits:
  • Smaller initial bundle
  • Faster initial load time
  • Code splitting by feature
  • Load features on-demand

404 Handling

The wildcard route catches all unmatched URLs:
{
  path: '**',
  component: NotFoundPageComponent
}
Important: Must be the last route in the configuration.

Route Configuration Best Practices

1. Route Hierarchy

// Parent route with layout
{
  path: 'feature',
  component: FeatureLayoutComponent,
  children: [
    { path: 'list', component: ListComponent },
    { path: 'detail/:id', component: DetailComponent }
  ]
}

2. Guard Composition

Guards can be combined:
{
  path: 'admin',
  canActivate: [authGuard, roleGuard([RolesEnum.ADMIN])]
}

3. Redirect Routes

// Redirect with full path match
{ path: '', redirectTo: '/home', pathMatch: 'full' }

// Redirect within feature
{ path: '', redirectTo: 'list', pathMatch: 'full' }

4. Route Ordering

More specific routes should come before generic ones:
// Correct order
{ path: 'tickets/nuevo', component: TicketFormComponent },
{ path: 'tickets/:id', component: TicketDetailComponent },
{ path: 'tickets', component: TicketsComponent }
Listen to routing events for loading indicators, etc.:
import { Router, NavigationStart, NavigationEnd } from '@angular/router';

constructor(private router: Router) {
  this.router.events.subscribe(event => {
    if (event instanceof NavigationStart) {
      // Show loading
    }
    if (event instanceof NavigationEnd) {
      // Hide loading
    }
  });
}

Next Steps

Build docs developers (and LLMs) love