Lesson 6: Advanced Angular Features.
Lesson 6: Advanced Angular Features
This lesson covers State Management with NgRx Effects, Reusable Modal Components, Dynamic Form Fields, Theme Switching with CSS Variables, and Debounced Search Input.
Example 1: State Management with NgRx Effects
Implements NgRx Effects to handle asynchronous API calls.
Code:
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { userReducer } from './user.reducer';
import { UserEffects } from './user.effects';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
HttpClientModule,
StoreModule.forRoot({ users: userReducer }),
EffectsModule.forRoot([UserEffects])
],
bootstrap: [AppComponent]
})
export class AppModule { }
// user.actions.ts
import { createAction, props } from '@ngrx/store';
export const loadUsers = createAction('[User] Load Users');
export const loadUsersSuccess = createAction('[User] Load Users Success', props<{ users: any[] }>());
// user.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { loadUsersSuccess } from './user.actions';
export const initialState: any[] = [];
export const userReducer = createReducer(
initialState,
on(loadUsersSuccess, (state, { users }) => users)
);
// user.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { HttpClient } from '@angular/common/http';
import { loadUsers, loadUsersSuccess } from './user.actions';
import { map, switchMap } from 'rxjs/operators';
@Injectable()
export class UserEffects {
constructor(private actions$: Actions, private http: HttpClient) {}
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(loadUsers),
switchMap(() => this.http.get<any[]>('https://jsonplaceholder.typicode.com/users')
.pipe(map(users => loadUsersSuccess({ users }))))
)
);
}
Explanation:
NgRx Store & Effects: Uses NgRx to manage state and side effects efficiently.
Asynchronous API Call:
UserEffectsfetches users from an API.Action Handling:
loadUserstriggers API fetch,loadUsersSuccessupdates the state.Immutable State: NgRx ensures state updates follow best practices.
Performance Optimization: Efficiently handles large-scale applications.
Example 2: Reusable Modal Component
Creates a reusable modal component.
Code:
// modal.component.ts
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-modal',
templateUrl: './modal.component.html',
styleUrls: ['./modal.component.css']
})
export class ModalComponent {
@Input() title = 'Modal Title';
@Output() close = new EventEmitter<void>();
closeModal() {
this.close.emit();
}
}
<!-- modal.component.html -->
<div class="overlay">
<div class="modal">
<h2>{{ title }}</h2>
<ng-content></ng-content>
<button (click)="closeModal()">Close</button>
</div>
</div>
/* modal.component.css */
.overlay {
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex; justify-content: center; align-items: center;
}
.modal {
background: white; padding: 20px; border-radius: 8px;
}
<!-- app.component.html -->
<button (click)="isModalOpen = true">Open Modal</button>
<app-modal *ngIf="isModalOpen" title="My Modal" (close)="isModalOpen = false">
<p>This is modal content.</p>
</app-modal>
Explanation:
Reusable Component:
app-modalcan be used anywhere in the app.Dynamic Content:
ng-contentallows inserting any content inside the modal.Two-Way Communication:
@Input()sets title,@Output()triggers closing.CSS Styling: Overlay ensures smooth modal display.
Conditional Rendering:
*ngIf="isModalOpen"controls modal visibility.
Example 3: Dynamic Form Fields
Adds form fields dynamically.
Code:
// app.component.ts
import { Component } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
items: this.fb.array([])
});
}
get items() {
return this.form.get('items') as FormArray;
}
addItem() {
this.items.push(this.fb.control('', Validators.required));
}
removeItem(index: number) {
this.items.removeAt(index);
}
}
<!-- app.component.html -->
<form [formGroup]="form">
<div formArrayName="items">
<div *ngFor="let item of items.controls; let i = index">
<input [formControlName]="i">
<button (click)="removeItem(i)">Remove</button>
</div>
</div>
<button (click)="addItem()">Add Item</button>
</form>
Explanation:
Dynamic Fields:
FormArrayallows adding/removing input fields dynamically.Validation: Each input requires a value before form submission.
Two-Way Binding: Updates instantly when adding/removing fields.
Scalability: Supports unlimited form fields.
Simplified UI: Uses
*ngForto dynamically create inputs.
Example 4: Theme Switching with CSS Variables
Implements dark/light mode switching.
Code:
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
toggleTheme() {
document.body.classList.toggle('dark-mode');
}
}
<!-- app.component.html -->
<button (click)="toggleTheme()">Toggle Theme</button>
/* styles.css */
:root {
--background: white;
--text: black;
}
.dark-mode {
--background: black;
--text: white;
}
body {
background-color: var(--background);
color: var(--text);
transition: 0.3s;
}
Explanation:
CSS Variables: Uses
--backgroundand--textto store colors.Class Toggle:
dark-modeapplies dark theme dynamically.Smooth Transitions: Theme switches seamlessly with
transition: 0.3s.Global Styling: Works across the entire application.
Performance: No component re-rendering needed.
Example 5: Debounced Search Input
Delays search execution until the user stops typing.
Code:
// app.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime } from 'rxjs/operators';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
searchControl = new FormControl('');
constructor() {
this.searchControl.valueChanges.pipe(debounceTime(500)).subscribe(value => {
console.log('Search:', value);
});
}
}
<!-- app.component.html -->
<input [formControl]="searchControl" placeholder="Type to search...">
Explanation:
Debounce Mechanism: Delays execution until user stops typing.
RxJS Integration: Uses
debounceTime(500)to optimize performance.Real-Time Search: Triggers API requests only when necessary.
FormControl Usage: Simplifies handling input changes.
Improves UX: Prevents unnecessary API calls.
These five Angular examples cover NgRx Effects, modals, dynamic forms, theme switching, and search debouncing, essential for modern web applications .