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:

  1. Lazy Loading Modules: The loadChildren syntax loads the module only when /dashboard is visited.

  2. Performance Boost: Reduces initial bundle size, improving app performance.

  3. Module Separation: DashboardModule encapsulates its own components and routing.

  4. On-Demand Loading: Loads features only when needed, enhancing user experience.

  5. 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:

  1. ReactiveFormsModule: Enables form handling with FormGroup and FormControl.

  2. Form Validation: Uses Validators.required and Validators.email to enforce input rules.

  3. Error Handling: *ngIf conditions display validation errors dynamically.

  4. FormBuilder Usage: Simplifies form creation using fb.group({}) syntax.

  5. 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:

  1. Mock Authentication: AuthService.login() returns a mock JWT token.

  2. State Management: The token is stored and displayed in the component.

  3. Encapsulation: The authentication logic is separated into AuthService.

  4. Conditional UI: *ngIf="token" shows login status dynamically.

  5. 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:

  1. Interceptor Role: Automatically attaches JWT tokens to every HTTP request.

  2. Cloning Requests: req.clone() prevents modifying the original request.

  3. SetHeaders Method: Adds Authorization: Bearer token dynamically.

  4. Global HTTP Handling: HTTP_INTERCEPTORS applies the interceptor globally.

  5. 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:

  1. DynamicComponent Creation: A separate component is loaded on demand.

  2. ViewContainerRef: Allows inserting components dynamically into the DOM.

  3. ComponentFactoryResolver: Creates component instances at runtime.

  4. Lazy Loading: Components are loaded only when required.

  5. 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.