Skip to content
All essays
WebMarch 27, 202513 min

Angular RxJS: Reactive Programming

Master RxJS in Angular. Learn operators, observables, subjects, and reactive patterns.

Ü
Ümit Uz
Mobile & Full Stack Developer

RxJS is a powerful library for reactive programming in Angular, enabling you to work with asynchronous data streams.

Observables

typescript
import { Observable } from 'rxjs';

// Creating observables
const data$ = new Observable(subscriber => {
  subscriber.next('Hello');
  subscriber.next('World');
  subscriber.complete();
});

// Subscribing
data$.subscribe({
  next: value => console.log(value),
  error: err => console.error(err),
  complete: () => console.log('Complete')
});

Common Creation Operators

typescript
import { of, from, interval, timer, fromEvent } from 'rxjs';

// of
const numbers$ = of(1, 2, 3, 4, 5);

// from
const array$ = from([1, 2, 3, 4, 5]);
const promise$ = from(fetch('/api/users'));

// interval
const seconds$ = interval(1000);

// timer
const delayed$ = timer(1000);
const delayedInterval$ = timer(1000, 500);

// fromEvent
const click$ = fromEvent(document, 'click');

Pipe and Operators

typescript
import { map, filter, tap } from 'rxjs/operators';

numbers$
  .pipe(
    filter(n => n % 2 === 0),
    map(n => n * 2),
    tap(n => console.log('Processing:', n))
  )
  .subscribe(result => console.log(result));

Common Operators

map

typescript
import { map } from 'rxjs/operators';

users$
  .pipe(
    map(users => users.map(user => user.name))
  )
  .subscribe(names => console.log(names));

filter

typescript
import { filter } from 'rxjs/operators';

numbers$
  .pipe(
    filter(n => n > 5)
  )
  .subscribe(largeNumbers => console.log(largeNumbers));

switchMap

typescript
import { switchMap } from 'rxjs/operators';

userId$
  .pipe(
    switchMap(id => this.http.get(`/api/users/${id}`))
  )
  .subscribe(user => console.log(user));

mergeMap

typescript
import { mergeMap } from 'rxjs/operators';

ids$
  .pipe(
    mergeMap(id => this.http.get(`/api/items/${id}`))
  )
  .subscribe(item => console.log(item));

debounceTime

typescript
import { debounceTime } from 'rxjs/operators';

searchInput$
  .pipe(
    debounceTime(300),
    distinctUntilChanged(),
    switchMap(query => this.search(query))
  )
  .subscribe(results => console.log(results));

catchError

typescript
import { catchError, of } from 'rxjs/operators';

this.http.get('/api/users')
  .pipe(
    catchError(error => {
      console.error('Error:', error);
      return of([]);
    })
  )
  .subscribe(users => console.log(users));

Subjects

BehaviorSubject

typescript
import { BehaviorSubject } from 'rxjs';

const user$ = new BehaviorSubject<User | null>(null);

user$.subscribe(user => console.log('User:', user));

user$.next({ name: 'John' });
user$.next({ name: 'Jane' });

// Get current value
console.log(user$.value);

ReplaySubject

typescript
import { ReplaySubject } from 'rxjs';

const events$ = new ReplaySubject(3); // Buffer last 3 values

events$.next('Event 1');
events$.next('Event 2');
events$.next('Event 3');

// Late subscribers receive buffered values
events$.subscribe(event => console.log(event));

AsyncSubject

typescript
import { AsyncSubject } from 'rxjs';

const result$ = new AsyncSubject();

result$.next('Value 1');
result$.next('Value 2');
result$.complete(); // Only emits last value on complete

Hot vs Cold Observables

typescript
// Cold - Each subscriber gets independent execution
const cold$ = new Observable(subscriber => {
  subscriber.next(Math.random());
});

// Hot - All subscribers share same execution
const hot$ = cold$.pipe(share());

Error Handling

typescript
import { catchError, retry, throwError } from 'rxjs';

this.http.get('/api/data')
  .pipe(
    retry(3),
    catchError(error => {
      if (error.status === 404) {
        return of([]);
      }
      return throwError(() => error);
    })
  )
  .subscribe();

HttpClient with RxJS

typescript
@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private http: HttpClient) {}

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/api/users')
      .pipe(
        catchError(this.handleError)
      );
  }

  getUser(id: number): Observable<User> {
    return this.http.get<User>(`/api/users/${id}`);
  }

  private handleError(error: HttpErrorResponse) {
    console.error('API Error:', error);
    return throwError(() => error);
  }
}

Component with RxJS

typescript
@Component({
  selector: 'app-users',
  template: `
    <div *ngFor="let user of users$ | async">
      {{ user.name }}
    </div>
  `
})
export class UsersComponent implements OnInit, OnDestroy {
  users$ = this.userService.getUsers();
  private destroy$ = new Subject<void>();

  constructor(
    private userService: UserService,
    private route: ActivatedRoute
  ) {}

  ngOnInit() {
    this.route.params
      .pipe(
        takeUntil(this.destroy$),
        switchMap(params => this.userService.getUser(params['id']))
      )
      .subscribe(user => console.log(user));
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

RxJS provides powerful tools for handling asynchronous operations in Angular applications.

Enjoyed this essay? Share it.

Related essays

Next essay
Angular Standalone Components