Lesson 4: Advanced Angular Concepts

Lesson 4: Advanced Angular Concepts

This lesson explores Angular Animations, WebSockets, Directives with Inputs, Multi-Step Forms, and Custom Validators.


Example 1: Angular Animations (Fade In & Out)

This example demonstrates Angular animations for fading elements in and out.

Code:

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, BrowserAnimationsModule],
  bootstrap: [AppComponent]
})
export class AppModule { }
// app.component.ts
import { Component } from '@angular/core';
import { trigger, state, style, animate, transition } from '@angular/animations';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('fade', [
      state('visible', style({ opacity: 1 })),
      state('hidden', style({ opacity: 0 })),
      transition('visible <=> hidden', animate('500ms ease-in-out'))
    ])
  ]
})
export class AppComponent {
  isVisible = true;

  toggle() {
    this.isVisible = !this.isVisible;
  }
}
<!-- app.component.html -->
<button (click)="toggle()">Toggle Fade</button>
<div [@fade]="isVisible ? 'visible' : 'hidden'" class="box">Hello, Angular!</div>
/* app.component.css */
.box {
  width: 200px;
  height: 100px;
  background-color: lightblue;
  text-align: center;
  line-height: 100px;
  margin-top: 20px;
}

Explanation:

  1. Animations Module: BrowserAnimationsModule is imported to enable Angular animations.

  2. Trigger & States: trigger('fade') defines animation states (visible, hidden).

  3. Transitions: transition('visible <=> hidden') animates state changes.

  4. Binding Animation: [@fade] dynamically controls the element's visibility.

  5. Smooth Effects: animate('500ms ease-in-out') ensures fluid transitions.


Example 2: Real-Time Data with WebSockets

Implements WebSockets for real-time chat messages.

Code:

// websocket.service.ts
import { Injectable } from '@angular/core';
import { Observable, Subject, WebSocketSubject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class WebSocketService {
  private socket$: WebSocketSubject<string>;

  constructor() {
    this.socket$ = new WebSocketSubject('wss://echo.websocket.org');
  }

  sendMessage(message: string) {
    this.socket$.next(message);
  }

  getMessages(): Observable<string> {
    return this.socket$.asObservable();
  }
}
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { WebSocketService } from './websocket.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  messages: string[] = [];
  newMessage = '';

  constructor(private wsService: WebSocketService) {}

  ngOnInit() {
    this.wsService.getMessages().subscribe(msg => {
      this.messages.push(msg);
    });
  }

  sendMessage() {
    this.wsService.sendMessage(this.newMessage);
    this.newMessage = '';
  }
}
<!-- app.component.html -->
<input [(ngModel)]="newMessage" placeholder="Type a message">
<button (click)="sendMessage()">Send</button>
<ul>
  <li *ngFor="let msg of messages">{{ msg }}</li>
</ul>

Explanation:

  1. WebSocketSubject: Handles WebSocket connections reactively.

  2. Observable Messages: getMessages() returns an observable stream of messages.

  3. Two-Way Data Flow: sendMessage() sends data to the WebSocket server.

  4. Dynamic UI Update: New messages appear in the list in real time.

  5. Reusable Service: WebSocketService can be injected anywhere.


Example 3: Directive with Input Property

A custom directive that changes text color dynamically.

Code:

// color.directive.ts
import { Directive, ElementRef, Input, OnChanges } from '@angular/core';

@Directive({
  selector: '[appColor]'
})
export class ColorDirective implements OnChanges {
  @Input() appColor = 'black';

  constructor(private el: ElementRef) {}

  ngOnChanges() {
    this.el.nativeElement.style.color = this.appColor;
  }
}
<!-- app.component.html -->
<p appColor="red">This text is red</p>
<p appColor="blue">This text is blue</p>
<p appColor="green">This text is green</p>

Explanation:

  1. Custom Directive: @Directive() creates a reusable directive.

  2. Dynamic Input: @Input() appColor allows dynamic color changes.

  3. ElementRef Manipulation: Directly modifies the host element’s style.

  4. Lifecycle Hook: ngOnChanges() updates color when appColor changes.

  5. Versatility: Works on any text element dynamically.


Example 4: Multi-Step Form (Stepper)

Creates a multi-step form for user registration.

Code:

// app.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  step = 1;
  user = { name: '', email: '', password: '' };

  next() {
    this.step++;
  }

  previous() {
    this.step--;
  }

  submit() {
    console.log('User Data:', this.user);
  }
}
<!-- app.component.html -->
<div *ngIf="step === 1">
  <h2>Step 1: Enter Name</h2>
  <input [(ngModel)]="user.name" placeholder="Name">
  <button (click)="next()">Next</button>
</div>

<div *ngIf="step === 2">
  <h2>Step 2: Enter Email</h2>
  <input [(ngModel)]="user.email" placeholder="Email">
  <button (click)="previous()">Back</button>
  <button (click)="next()">Next</button>
</div>

<div *ngIf="step === 3">
  <h2>Step 3: Enter Password</h2>
  <input type="password" [(ngModel)]="user.password" placeholder="Password">
  <button (click)="previous()">Back</button>
  <button (click)="submit()">Submit</button>
</div>

Explanation:

  1. Step Management: step variable controls which form step is displayed.

  2. Two-Way Binding: [(ngModel)] updates user object properties dynamically.

  3. Navigation Control: next() and previous() switch between steps.

  4. Conditional Rendering: *ngIf="step === x" ensures step-wise form flow.

  5. Form Submission: submit() logs user details when completed.


Example 5: Custom Form Validator (Password Match)

A custom validator ensures passwords match.

Code:

// password-match.directive.ts
import { AbstractControl, ValidatorFn } from '@angular/forms';

export function passwordMatchValidator(): ValidatorFn {
  return (control: AbstractControl) => {
    const password = control.get('password')?.value;
    const confirmPassword = control.get('confirmPassword')?.value;
    return password === confirmPassword ? null : { mismatch: true };
  };
}
// app.component.ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { passwordMatchValidator } from './password-match.directive';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      password: ['', Validators.required],
      confirmPassword: ['', Validators.required]
    }, { validators: passwordMatchValidator() });
  }
}
<!-- app.component.html -->
<form [formGroup]="form">
  <input type="password" formControlName="password" placeholder="Password">
  <input type="password" formControlName="confirmPassword" placeholder="Confirm Password">
  <div *ngIf="form.errors?.mismatch">Passwords do not match!</div>
</form>

Explanation:

  1. Custom Validator: passwordMatchValidator() ensures both fields match.

  2. FormGroup Validation: Applied to entire form, not individual fields.

  3. Dynamic Error Display: *ngIf="form.errors?.mismatch" conditionally shows validation errors.

  4. FormBuilder Usage: Simplifies form control creation.

  5. Security Enhancement: Prevents mismatched password submissions.


These five Angular examples cover animations, WebSockets, directives, multi-step forms, and validation, essential for advanced applications