Routing & Forms¶
Routing: Angular's RouterModule maps URL paths to components. Supports lazy loading, route guards, and nested routes. Forms: Two approaches — Template-driven (simpler, uses ngModel) and Reactive (more control, uses FormGroup/FormControl in TypeScript). Reactive forms are preferred for complex validation.
RouterModule & Configuration¶
Define routes as an array of { path, component } objects. Use RouterModule.forRoot(routes) in the root module. <router-outlet> is the placeholder where routed components render. Navigate with routerLink directive or Router.navigate().
Deep Dive: Route Config & Navigation
// app-routing.module.ts
const routes: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'users', component: UserListComponent },
{ path: 'users/:id', component: UserDetailComponent }, // route param
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] },
{ path: '**', component: NotFoundComponent } // wildcard — 404
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Lazy Loading¶
Lazy loading loads feature modules on demand (only when the user navigates to that route). Reduces initial bundle size. Use loadChildren with dynamic import syntax. Each lazy-loaded module has its own routing with RouterModule.forChild().
Deep Dive: Example
// app-routing.module.ts
const routes: Routes = [
{ path: 'home', component: HomeComponent },
{
path: 'admin',
loadChildren: () => import('./admin/admin.module')
.then(m => m.AdminModule) // lazy-loaded!
}
];
// admin/admin-routing.module.ts
const adminRoutes: Routes = [
{ path: '', component: AdminDashboardComponent },
{ path: 'users', component: AdminUsersComponent }
];
@NgModule({
imports: [RouterModule.forChild(adminRoutes)] // forChild, not forRoot
})
export class AdminRoutingModule { }
Route Guards¶
Guards control access to routes. CanActivate — can the user navigate to this route? CanDeactivate — can the user leave? (unsaved changes). Resolve — pre-fetch data before route loads. Guards return boolean, Observable<boolean>, or UrlTree (redirect).
Deep Dive: AuthGuard Example
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot): boolean | UrlTree {
if (this.authService.isLoggedIn()) {
return true;
}
return this.router.createUrlTree(['/login']); // redirect
}
}
// Usage in routes
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }
Template-driven Forms¶
Uses FormsModule. Two-way binding with [(ngModel)]. Validation via HTML attributes (required, minlength). Angular auto-creates NgForm and NgModel directives. Simpler for basic forms but harder to test and scale.
Deep Dive: Example
<form #userForm="ngForm" (ngSubmit)="onSubmit(userForm)">
<input
name="name"
[(ngModel)]="user.name"
required
minlength="3"
#name="ngModel">
<div *ngIf="name.invalid && name.touched" class="error">
<span *ngIf="name.errors?.['required']">Name is required</span>
<span *ngIf="name.errors?.['minlength']">Min 3 characters</span>
</div>
<input name="email" [(ngModel)]="user.email" required email #email="ngModel">
<button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>
Reactive Forms¶
Uses ReactiveFormsModule. Form structure defined in TypeScript with FormGroup, FormControl, FormArray. Validation via Validators. More powerful: dynamic forms, custom validators, easier testing. Preferred for complex forms.
Deep Dive: Example
@Component({ selector: 'app-user-form' })
export class UserFormComponent implements OnInit {
userForm!: FormGroup;
constructor(private fb: FormBuilder) {}
ngOnInit() {
this.userForm = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
address: this.fb.group({ // nested group
street: [''],
city: ['', Validators.required]
}),
hobbies: this.fb.array([]) // dynamic array
});
}
addHobby() {
(this.userForm.get('hobbies') as FormArray)
.push(this.fb.control('', Validators.required));
}
onSubmit() {
if (this.userForm.valid) {
console.log(this.userForm.value);
}
}
}
<form [formGroup]="userForm" (ngSubmit)="onSubmit()">
<input formControlName="name">
<div *ngIf="userForm.get('name')?.invalid && userForm.get('name')?.touched">
Name is required (min 3 chars)
</div>
<input formControlName="email">
<div formGroupName="address">
<input formControlName="street" placeholder="Street">
<input formControlName="city" placeholder="City">
</div>
<button type="submit" [disabled]="userForm.invalid">Submit</button>
</form>
| Feature | Template-driven | Reactive |
|---|---|---|
| Module | FormsModule |
ReactiveFormsModule |
| Binding | [(ngModel)] |
formControlName |
| Structure | Template | TypeScript class |
| Validation | HTML attributes | Validators class |
| Testing | Harder | Easier |
| Best for | Simple forms | Complex, dynamic forms |
Common Interview Questions¶
Common Interview Questions
- How does Angular routing work?
- What is lazy loading? How do you implement it?
- What are route guards? Name the types.
- What is the difference between template-driven and reactive forms?
- How do you validate forms in Angular?
- What is
FormGroup,FormControl,FormArray? - What is
routerLinkvsRouter.navigate()? - How do you read route parameters?