Lesson 3: Angular Advanced Topics
Lesson 3: Angular Advanced Topics
This lesson covers Lazy Loading, Reactive Forms, Authentication with JWT, Interceptors, and Dynamic Components in Angular.
Example 1: Lazy Loading Modules in Angular
Uses lazy loading to split modules and load them on demand.
Code:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{ path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule) },
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
// dashboard.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DashboardComponent } from './dashboard.component';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [{ path: '', component: DashboardComponent }];
@NgModule({
declarations: [DashboardComponent],
imports: [CommonModule, RouterModule.forChild(routes)]
})
export class DashboardModule { }
// dashboard.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
template: `<h2>Lazy Loaded Dashboard</h2>`,
styles: ['h2 { color: blue; }']
})
export class DashboardComponent { }
Explanation:
Lazy Loading Modules: The
loadChildren
syntax loads the module only when/dashboard
is visited.Performance Boost: Reduces initial bundle size, improving app performance.
Module Separation:
DashboardModule
encapsulates its own components and routing.On-Demand Loading: Loads features only when needed, enhancing user experience.
Routing Management:
RouterModule.forChild(routes)
registers module-specific routes.
Example 2: Reactive Forms with Validation
A login form using Reactive Forms and FormGroup validation.
Code:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, ReactiveFormsModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
loginForm: FormGroup;
constructor(private fb: FormBuilder) {
this.loginForm = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]]
});
}
login() {
if (this.loginForm.valid) {
console.log('Login Successful:', this.loginForm.value);
}
}
}
<!-- app.component.html -->
<form [formGroup]="loginForm" (ngSubmit)="login()">
<label>Email:</label>
<input type="email" formControlName="email">
<div *ngIf="loginForm.controls.email.invalid && loginForm.controls.email.touched">Invalid Email</div>
<label>Password:</label>
<input type="password" formControlName="password">
<div *ngIf="loginForm.controls.password.invalid && loginForm.controls.password.touched">Min 6 characters</div>
<button type="submit" [disabled]="loginForm.invalid">Login</button>
</form>
Explanation:
ReactiveFormsModule: Enables form handling with
FormGroup
andFormControl
.Form Validation: Uses
Validators.required
andValidators.email
to enforce input rules.Error Handling:
*ngIf
conditions display validation errors dynamically.FormBuilder Usage: Simplifies form creation using
fb.group
({})
syntax.Submit Handling: Logs form values only if all validations pass.
Example 3: Authentication with JWT Tokens
Implements user authentication using a mock JWT-based login system.
Code:
// auth.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class AuthService {
login(username: string, password: string): string | null {
if (username === 'admin' && password === 'password') {
return 'mock-jwt-token-123456';
}
return null;
}
}
// app.component.ts
import { Component } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
token: string | null = null;
constructor(private authService: AuthService) {}
login() {
this.token = this.authService.login('admin', 'password');
}
}
<!-- app.component.html -->
<button (click)="login()">Login</button>
<p *ngIf="token">Authenticated! Token: {{ token }}</p>
Explanation:
Mock Authentication:
AuthService.login()
returns a mock JWT token.State Management: The token is stored and displayed in the component.
Encapsulation: The authentication logic is separated into
AuthService
.Conditional UI:
*ngIf="token"
shows login status dynamically.Scalability: The service can later integrate real backend authentication.
Example 4: HTTP Interceptor for Token Authentication
Adds a global HTTP interceptor to attach JWT tokens.
Code:
// auth.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler) {
const token = 'mock-jwt-token-123456';
const cloned = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
return next.handle(cloned);
}
}
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './auth.interceptor';
@NgModule({
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
],
imports: [BrowserModule],
bootstrap: []
})
export class AppModule { }
Explanation:
Interceptor Role: Automatically attaches JWT tokens to every HTTP request.
Cloning Requests:
req.clone()
prevents modifying the original request.SetHeaders Method: Adds
Authorization: Bearer
token dynamically.Global HTTP Handling:
HTTP_INTERCEPTORS
applies the interceptor globally.Security: Ensures all API calls include authentication credentials.
Example 5: Dynamic Component Loading
Loads components dynamically at runtime.
Code:
// dynamic.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-dynamic',
template: `<h3>This is a dynamically loaded component!</h3>`,
})
export class DynamicComponent { }
// app.component.ts
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { DynamicComponent } from './dynamic.component';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
@ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) {}
loadComponent() {
this.container.clear();
const factory = this.resolver.resolveComponentFactory(DynamicComponent);
this.container.createComponent(factory);
}
}
<!-- app.component.html -->
<button (click)="loadComponent()">Load Component</button>
<div #container></div>
Explanation:
DynamicComponent Creation: A separate component is loaded on demand.
ViewContainerRef: Allows inserting components dynamically into the DOM.
ComponentFactoryResolver: Creates component instances at runtime.
Lazy Loading: Components are loaded only when required.
Memory Management:
container.clear()
prevents memory leaks.
These five Angular examples cover lazy loading, authentication, forms, HTTP interceptors, and dynamic components, essential for scalable applications.