Routing Overview
The Trippins frontend uses Angular Router for client-side navigation with route guards for authentication and authorization. All routes are prefixed with /new/ for API versioning compatibility.
Public Routes Accessible to all users
Protected Routes Require authentication
Admin Routes Require ROLE_ADMIN
Guards Route protection logic
Route Configuration
The main routing module defines all application routes:
src/app/app-routing.module.ts
import { NgModule } from '@angular/core' ;
import { RouterModule , Routes } from '@angular/router' ;
import { RoomComponent } from './components/room/room.component' ;
import { AboutComponent } from './components/about/about.component' ;
import { AdminComponent } from './components/admin/admin.component' ;
import { ErrorComponent } from './components/error/error.component' ;
import { IndexComponent } from './components/index/index.component' ;
import { LoginComponent } from './components/login/login.component' ;
import { NewhotelComponent } from './components/newhotel/newhotel.component' ;
import { ProfileComponent } from './components/profile/profile.component' ;
import { RegisterComponent } from './components/register/register.component' ;
import { RoomDetailsComponent } from './components/room-details/room-details.component' ;
import { authGuard } from './guards/auth.guard' ;
import { roleGuard } from './guards/role.guard' ;
const routes : Routes = [
{ path: '' , component: IndexComponent },
{ path: 'new/home' , redirectTo: '' , pathMatch: 'full' },
{ path: 'new/index' , redirectTo: '' , pathMatch: 'full' },
// Public routes
{ path: 'new/about' , component: AboutComponent },
{ path: 'new/login' , component: LoginComponent },
{ path: 'new/register' , component: RegisterComponent },
{ path: 'new/rooms' , component: RoomComponent },
{ path: 'new/rooms/:id' , component: RoomDetailsComponent , canActivate: [ authGuard ] },
// Protected user routes
{
path: 'new/profile' ,
component: ProfileComponent ,
canActivate: [ authGuard ]
},
// Admin routes
{
path: 'new/admin' ,
component: AdminComponent ,
canActivate: [ authGuard , roleGuard ],
data: { roles: [ "ROLE_ADMIN" ] }
},
{
path: 'new/housing/creation' ,
component: NewhotelComponent ,
canActivate: [ authGuard ]
},
// Error route
{ path: 'new/error' , component: ErrorComponent },
// Wildcard route (must be last)
{ path: '**' , redirectTo: 'error' }
];
@ NgModule ({
imports: [ RouterModule . forRoot ( routes )],
exports: [ RouterModule ]
})
export class AppRoutingModule { }
Route Types
Public Routes
Accessible without authentication:
Path Component Description /IndexComponent Landing page /new/homeRedirect to / Home alias /new/aboutAboutComponent About page /new/loginLoginComponent Login form /new/registerRegisterComponent Registration form /new/roomsRoomComponent Hotel listings
Multiple paths can redirect to the same component using redirectTo with pathMatch: 'full' for exact matching.
Protected Routes (Authentication Required)
Require user to be logged in via authGuard:
Path Component Guard Description /new/rooms/:idRoomDetailsComponent authGuard Individual room details /new/profileProfileComponent authGuard User profile /new/housing/creationNewhotelComponent authGuard Create hotel listing
Admin Routes (Role-Based Access)
Require both authentication AND admin role:
Path Component Guards Required Role Description /new/adminAdminComponent authGuard, roleGuard ROLE_ADMIN Admin dashboard
Route data can pass configuration to guards. The admin route passes data: { roles: ["ROLE_ADMIN"] } to the roleGuard.
Route Parameters
Path Parameters
Extract dynamic segments from the URL:
Route Definition
Component Usage
{
path : 'new/rooms/:id' ,
component : RoomDetailsComponent ,
canActivate : [ authGuard ]
}
Query Parameters
Handle optional URL parameters:
Setting Query Params
Reading Query Params
this . router . navigate ([ 'new/login' ], {
queryParams: { returnUrl: '/new/profile' }
});
// Results in: /new/login?returnUrl=%2Fnew%2Fprofile
Query parameters are useful for return URLs after login, allowing users to continue where they left off.
Route Guards
Authentication Guard
Functional guard that checks if user is logged in:
src/app/guards/auth.guard.ts
import { inject } from '@angular/core' ;
import { ActivatedRouteSnapshot , CanActivateFn , Router , RouterStateSnapshot } from '@angular/router' ;
import { AuthService } from '../services/auth.service' ;
export const authGuard : CanActivateFn = (
route : ActivatedRouteSnapshot ,
state : RouterStateSnapshot
) => {
const authService = inject ( AuthService );
const router = inject ( Router );
if ( authService . isLoggedIn ()) {
return true ;
}
// Redirect to login page with return URL
return router . createUrlTree ([ 'new/login' ], {
queryParams: { returnUrl: state . url }
});
};
Inject Dependencies : Uses Angular’s inject() function to get services
Check Authentication : Calls authService.isLoggedIn() to verify JWT token exists
Allow Access : Returns true if authenticated
Redirect : Returns a UrlTree to redirect unauthenticated users to login
Preserve Destination : Adds returnUrl query parameter for post-login redirect
Role-Based Guard
Checks if user has required roles for admin access:
src/app/guards/role.guard.ts
import { ActivatedRouteSnapshot , CanActivateFn , Router , RouterStateSnapshot } from '@angular/router' ;
import { AuthService } from '../services/auth.service' ;
import { inject } from '@angular/core' ;
export const roleGuard : CanActivateFn = (
route : ActivatedRouteSnapshot ,
state : RouterStateSnapshot
) => {
const authService = inject ( AuthService );
const router = inject ( Router );
const requiredRoles = route . data [ 'roles' ] as string [];
if ( authService . hasAnyRole ( requiredRoles )) {
return true ;
}
return router . createUrlTree ([ 'new/error' ]);
};
Extract Required Roles : Gets roles from route data configuration
Check User Roles : Calls authService.hasAnyRole(requiredRoles)
Allow Access : Returns true if user has at least one required role
Deny Access : Redirects to error page if user lacks permissions
Guard Execution Order
When multiple guards are applied, they execute in array order:
{
path : 'new/admin' ,
component : AdminComponent ,
canActivate : [ authGuard , roleGuard ], // Executes authGuard first, then roleGuard
data : { roles : [ "ROLE_ADMIN" ] }
}
authGuard Runs First
Verifies user is logged in
If Auth Passes, roleGuard Runs
Checks if user has required role
Both Must Pass
User must be authenticated AND have admin role
Navigation Methods
Programmatic Navigation
Navigate by Path
Navigate by URL String
Navigate with Parameters
import { Router } from '@angular/router' ;
export class HeaderComponent {
constructor ( private router : Router ) {}
logout () : void {
this . authService . logout ();
this . router . navigate ([ 'new/login' ]);
}
}
Template Navigation
Basic RouterLink
RouterLink with Parameters
Active Route Styling
< a routerLink = "/new/about" > About Us </ a >
< a [routerLink] = "['/new/rooms']" > View Rooms </ a >
routerLinkActive automatically adds CSS classes when the route is active, useful for navigation highlighting.
Route Events
Listen to navigation events for loading indicators or analytics:
import { Router , NavigationStart , NavigationEnd , NavigationError } from '@angular/router' ;
export class AppComponent implements OnInit {
constructor ( private router : Router ) {}
ngOnInit () {
this . router . events . subscribe ( event => {
if ( event instanceof NavigationStart ) {
// Show loading spinner
console . log ( 'Navigation started:' , event . url );
}
if ( event instanceof NavigationEnd ) {
// Hide loading spinner
console . log ( 'Navigation completed:' , event . urlAfterRedirects );
}
if ( event instanceof NavigationError ) {
// Handle navigation error
console . error ( 'Navigation error:' , event . error );
}
});
}
}
Wildcard Route
Catch-all route for 404 errors (must be last):
{ path : '**' , redirectTo : 'error' }
The wildcard route must be the last route in the configuration. Routes are evaluated in order, so ** will match everything if placed earlier.
Route Resolution
Route Resolvers
Pre-fetch data before navigating to a route (not currently implemented, but recommended pattern):
import { inject } from '@angular/core' ;
import { ResolveFn } from '@angular/router' ;
import { HousingServiceService } from '../services/housing-service.service' ;
export const roomResolver : ResolveFn < HousingDTO > = ( route , state ) => {
const housingService = inject ( HousingServiceService );
const id = Number ( route . paramMap . get ( 'id' ));
return housingService . getSpecificRoom ( id );
};
{
path : 'new/rooms/:id' ,
component : RoomDetailsComponent ,
resolve : { room : roomResolver },
canActivate : [ authGuard ]
}
Resolvers ensure data is loaded before the component renders, preventing blank screens during data fetching.
Authentication Flow with Routing
User Attempts Protected Route
User navigates to /new/profile
authGuard Executes
Guard checks if JWT token exists in localStorage
Not Authenticated
User redirected to /new/login?returnUrl=/new/profile
User Logs In
LoginComponent submits credentials to backend
Token Stored
AuthService stores JWT token and roles
Redirect to Original URL
User navigated back to /new/profile
onSubmit (): void {
this . authService . login ({ email , password }). subscribe ({
next : () => {
// Extract return URL from query params
const returnUrl = this . router . parseUrl ( this . router . url )
. queryParams [ 'returnUrl' ] || '/' ;
this . router . navigateByUrl ( returnUrl );
}
});
}
Admin Authorization Flow
User Attempts Admin Route
User navigates to /new/admin
authGuard Executes
Verifies user is logged in (has JWT)
roleGuard Executes
Checks if user has ROLE_ADMIN in roles array
Access Granted or Denied
Admin users see dashboard; others redirected to /new/error
const requiredRoles = route . data [ 'roles' ] as string []; // ["ROLE_ADMIN"]
if ( authService . hasAnyRole ( requiredRoles )) {
return true ; // User has admin role
}
return router . createUrlTree ([ 'new/error' ]); // Access denied
Lazy Loading (Recommended Future Enhancement)
For better performance, implement lazy loading for feature modules:
const routes : Routes = [
{
path: 'admin' ,
loadChildren : () => import ( './admin/admin.module' ). then ( m => m . AdminModule ),
canActivate: [ authGuard , roleGuard ],
data: { roles: [ "ROLE_ADMIN" ] }
}
];
Lazy loading splits code into separate bundles that are loaded on-demand, reducing initial page load time.
Route Configuration Best Practices
Group related routes together
Use consistent naming conventions
Document route purposes with comments
Keep wildcard route last
Apply guards at the highest applicable level
Combine guards for layered security
Use route data for guard configuration
Handle unauthorized access gracefully
Use routerLink in templates when possible
Prefer router.navigate() over navigateByUrl() for type safety
Always handle navigation errors
Preserve query parameters when needed
Testing Routes and Guards
Testing Guards
import { TestBed } from '@angular/core/testing' ;
import { Router } from '@angular/router' ;
import { authGuard } from './auth.guard' ;
import { AuthService } from '../services/auth.service' ;
describe ( 'authGuard' , () => {
let authService : jasmine . SpyObj < AuthService >;
let router : jasmine . SpyObj < Router >;
beforeEach (() => {
const authServiceSpy = jasmine . createSpyObj ( 'AuthService' , [ 'isLoggedIn' ]);
const routerSpy = jasmine . createSpyObj ( 'Router' , [ 'createUrlTree' ]);
TestBed . configureTestingModule ({
providers: [
{ provide: AuthService , useValue: authServiceSpy },
{ provide: Router , useValue: routerSpy }
]
});
authService = TestBed . inject ( AuthService ) as jasmine . SpyObj < AuthService >;
router = TestBed . inject ( Router ) as jasmine . SpyObj < Router >;
});
it ( 'should allow access when user is logged in' , () => {
authService . isLoggedIn . and . returnValue ( true );
const result = authGuard ( null as any , { url: '/new/profile' } as any );
expect ( result ). toBe ( true );
});
it ( 'should redirect to login when user is not logged in' , () => {
authService . isLoggedIn . and . returnValue ( false );
authGuard ( null as any , { url: '/new/profile' } as any );
expect ( router . createUrlTree ). toHaveBeenCalledWith (
[ 'new/login' ],
{ queryParams: { returnUrl: '/new/profile' } }
);
});
});
Common Routing Patterns
Breadcrumb Navigation
import { ActivatedRoute , NavigationEnd , Router } from '@angular/router' ;
import { filter } from 'rxjs/operators' ;
export class BreadcrumbComponent implements OnInit {
breadcrumbs : Array <{ label : string ; url : string }> = [];
constructor ( private router : Router , private route : ActivatedRoute ) {}
ngOnInit () {
this . router . events
. pipe ( filter ( event => event instanceof NavigationEnd ))
. subscribe (() => {
this . breadcrumbs = this . createBreadcrumbs ( this . route . root );
});
}
private createBreadcrumbs ( route : ActivatedRoute , url : string = '' , breadcrumbs : any [] = []) : any [] {
const children : ActivatedRoute [] = route . children ;
if ( children . length === 0 ) {
return breadcrumbs ;
}
for ( const child of children ) {
const routeURL : string = child . snapshot . url . map ( segment => segment . path ). join ( '/' );
if ( routeURL !== '' ) {
url += `/ ${ routeURL } ` ;
}
const label = child . snapshot . data [ 'breadcrumb' ];
if ( label ) {
breadcrumbs . push ({ label , url });
}
return this . createBreadcrumbs ( child , url , breadcrumbs );
}
return breadcrumbs ;
}
}
Next Steps
Angular Services Explore HTTP services and business logic
Component Architecture Learn about component structure