Lesson 2: Angular Advanced Concepts
Lesson 2: Angular Advanced Concepts
This lesson covers Directives, HTTP Requests, Observables, State Management, and Pipes in Angular.
Example 1: Custom Directive for Text Highlighting
A custom directive dynamically changes the text color on hover.
Code:
// highlight.directive.ts
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() highlightColor = 'yellow';
constructor(private el: ElementRef) {}
@HostListener('mouseenter') onMouseEnter() {
this.el.nativeElement.style.backgroundColor = this.highlightColor;
}
@HostListener('mouseleave') onMouseLeave() {
this.el.nativeElement.style.backgroundColor = '';
}
}
// app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent { }
<!-- app.component.html -->
<p appHighlight highlightColor="lightblue">Hover over this text to see the highlight effect.</p>
<p appHighlight highlightColor="lightgreen">Another example of directive usage.</p>
Explanation:
Custom Directive:
HighlightDirective
uses@Directive()
to modify element behavior dynamically.Element Reference:
ElementRef
grants access to the HTML element’s properties.Event Listeners:
@HostListener()
listens formouseenter
andmouseleave
to change styles.Input Binding:
@Input()
allows customizable highlight colors.Reusability: The directive can be applied to any element across multiple components.
Example 2: Fetching Data using HTTPClient (REST API Call)
This example retrieves users from a REST API and displays them.
Code:
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UserService {
private apiUrl = 'https://jsonplaceholder.typicode.com/users';
constructor(private http: HttpClient) {}
getUsers(): Observable<any> {
return this.http.get(this.apiUrl);
}
}
// app.component.ts
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
users: any[] = [];
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.getUsers().subscribe(data => {
this.users = data;
});
}
}
<!-- app.component.html -->
<ul>
<li *ngFor="let user of users">{{ user.name }} - {{ user.email }}</li>
</ul>
Explanation:
Service & Dependency Injection:
UserService
fetches API data and is injected into the component.HTTPClientModule: Uses
HttpClient
to make API calls.Observable & Subscription:
getUsers()
returns anObservable
which is subscribed to inngOnInit()
.Async Data Handling: Users list updates dynamically after API response.
Template Binding:
*ngFor
dynamically renders user data.
Example 3: Observables & Reactive Programming
Demonstrates Observables with a real-time counter.
Code:
// counter.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class CounterService {
private counter = new BehaviorSubject<number>(0);
counter$ = this.counter.asObservable();
increment() {
this.counter.next(this.counter.value + 1);
}
decrement() {
this.counter.next(this.counter.value - 1);
}
}
// app.component.ts
import { Component } from '@angular/core';
import { CounterService } from './counter.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
counterValue = 0;
constructor(private counterService: CounterService) {
this.counterService.counter$.subscribe(value => {
this.counterValue = value;
});
}
increase() {
this.counterService.increment();
}
decrease() {
this.counterService.decrement();
}
}
<!-- app.component.html -->
<h2>Counter: {{ counterValue }}</h2>
<button (click)="increase()">+</button>
<button (click)="decrease()">-</button>
Explanation:
BehaviorSubject:
counter$
holds an initial value and updates components in real time.Reactive State Management:
BehaviorSubject.next
()
updates the counter value reactively.Component Subscription:
subscribe()
listens for counter changes.Event-Driven Updates: Clicking buttons triggers
increment()
anddecrement()
.Reactive UI: Changes are reflected dynamically without manual refresh.
Example 4: State Management with NgRx Store
Uses NgRx Store for managing a global counter state.
Code:
// counter.actions.ts
import { createAction } from '@ngrx/store';
export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');
// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import { increment, decrement } from './counter.actions';
export const initialState = 0;
export const counterReducer = createReducer(
initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1)
);
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, StoreModule.forRoot({ counter: counterReducer })],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
// app.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { increment, decrement } from './counter.actions';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
counter$ = this.store.select('counter');
constructor(private store: Store<{ counter: number }>) {}
increase() {
this.store.dispatch(increment());
}
decrease() {
this.store.dispatch(decrement());
}
}
<!-- app.component.html -->
<h2>Counter: {{ counter$ | async }}</h2>
<button (click)="increase()">+</button>
<button (click)="decrease()">-</button>
Explanation:
State Management: NgRx
StoreModule
manages the counter state globally.Actions & Reducers:
increment
anddecrement
modify the state usingcounterReducer
.Immutable State: State updates are managed reactively.
Selector Subscription:
counter$ | async
automatically updates UI.Scalability: NgRx is ideal for large applications needing predictable state changes.
Example 5: Custom Pipe for Formatting Text
A custom Angular Pipe transforms text into uppercase.
Code:
// uppercase.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'uppercasePipe'
})
export class UppercasePipe implements PipeTransform {
transform(value: string): string {
return value.toUpperCase();
}
}
<!-- app.component.html -->
<p>{{ 'hello world' | uppercasePipe }}</p>
Explanation:
Pipe Creation:
UppercasePipe
transforms text using@Pipe()
.PipeTransform Interface: Implements
transform(value: string)
to modify text.Reusability: Can be applied across multiple components.
Simple Usage: Used in templates via
{{ value | uppercasePipe }}
.Performance: Runs efficiently with Angular’s change detection.
These five advanced Angular examples cover essential concepts for real-world applications