Advanced RxJS Patterns in Angular
Advanced RxJS Patterns in Angular
RxJS is a powerful tool for handling reactive programming in
Angular applications. Beyond basic observables, RxJS provides advanced patterns
that optimize performance, enhance user experience, and simplify
complex workflows.
In this guide, we’ll cover:
- Implementing
Infinite Scrolling with RxJS
- Optimizing
Performance with shareReplay
- Handling
User Authentication Streams
1. Implementing Infinite Scrolling with RxJS
Infinite scrolling is a common UI pattern where more data loads
automatically as the user scrolls. Instead of making multiple API requests
unnecessarily, we can use RxJS with Intersection Observer for
an optimized experience.
Step 1: Set Up a Service for API Calls
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class PostService {
private apiUrl =
'https://jsonplaceholder.typicode.com/posts';
constructor(private http: HttpClient) {}
getPosts(page: number, limit: number):
Observable<any> {
return
this.http.get(`${this.apiUrl}?_page=${page}&_limit=${limit}`);
}
}
Step 2: Implement Infinite Scrolling in a Component
import { Component, ElementRef, ViewChild, AfterViewInit } from
'@angular/core';
import { PostService } from './post.service';
import { fromEvent, merge, of } from 'rxjs';
import { switchMap, scan, startWith } from 'rxjs/operators';
@Component({
selector: 'app-infinite-scroll',
template: `
<div *ngFor="let post of
posts">{{ post.title }}</div>
<div #loadMore style="height:
50px; background: lightgray;">Loading...</div>
`
})
export class InfiniteScrollComponent implements AfterViewInit {
@ViewChild('loadMore', { static: false }) loadMore!:
ElementRef;
posts: any[] = [];
currentPage = 1;
limit = 10;
constructor(private postService: PostService) {}
ngAfterViewInit(): void {
const observer = new
IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
this.loadMorePosts();
}
});
observer.observe(this.loadMore.nativeElement);
}
loadMorePosts() {
this.postService.getPosts(this.currentPage++,
this.limit).subscribe(data => {
this.posts = [...this.posts,
...data];
});
}
}
Why This Works?
Optimized API Calls – Only fetches new data when needed.
Efficient Scrolling – Uses Intersection Observer instead
of manually tracking scroll position.
2. Optimizing Performance with shareReplay
Repeated API calls for the same data can slow down your app. shareReplay caches
the latest emitted values, preventing redundant requests.
Example: Caching API Calls with shareReplay
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, shareReplay } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UserService {
private userData$: Observable<any>;
constructor(private http: HttpClient) {
this.userData$ =
this.http.get('https://api.example.com/user').pipe(
shareReplay(1) // Caches the
latest response
);
}
getUser(): Observable<any> {
return this.userData$;
}
}
Usage in a Component
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-user',
template: `<div>{{ user$ | async | json
}}</div>`
})
export class UserComponent implements OnInit {
user$ = this.userService.getUser(); // Cached API
response
constructor(private userService: UserService) {}
ngOnInit() {}
}
Why Use shareReplay(1)?
Prevents Duplicate API Calls – Only calls the API once,
then shares the response.
Improves Performance – Reduces server load.
Ideal for Shared Data – Works great for user profiles,
settings, or static data.
3. Handling User Authentication Streams
User authentication is often managed using RxJS Behaviors,
ensuring a real-time authentication state across multiple
components.
Step 1: Create an Authentication Service
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private authState = new
BehaviorSubject<boolean>(false);
authState$ = this.authState.asObservable(); // Expose
Observable
login() {
// Simulate API login
setTimeout(() => {
this.authState.next(true);
}, 1000);
}
logout() {
this.authState.next(false);
}
}
Step 2: Use Authentication Stream in a Component
import { Component } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'app-auth',
template: `
<button *ngIf="!(auth$ |
async)" (click)="login()">Login</button>
<button *ngIf="auth$ | async"
(click)="logout()">Logout</button>
`
})
export class AuthComponent {
auth$ = this.authService.authState$;
constructor(private authService: AuthService) {}
login() {
this.authService.login();
}
logout() {
this.authService.logout();
}
}
Why Use RxJS for Authentication?
Real-time Authentication Updates – Components auto-update
when login state changes.
Better State Management – Avoids manual event listeners.
Simplifies Authentication Logic – Centralizes state handling.
Conclusion
RxJS
Pattern |
Use
Case |
Infinite
Scrolling with Intersection Observer |
Optimized
lazy loading of data |
shareReplay
for Performance Optimization |
Prevents
unnecessary API calls and caches responses |
RxJS
Authentication Streams |
Manages
login state reactively |
RxJS provides powerful and efficient solutions
for handling complex UI behaviors in Angular applications. By
using these advanced patterns, you can create a faster, scalable, and
more maintainable app.
Comments
Post a Comment