Working with asynchronous data is a core part of building modern web applications. In Angular, RxJS Observables provide a powerful way to handle async data streams—whether you're dealing with HTTP requests, user events, or real-time updates.
In this tutorial, you'll learn how to use RxJS Observables in Angular, including how to create and subscribe to Observables, use common RxJS operators, and apply best practices for managing asynchronous data. We’ll walk through practical examples that demonstrate how Observables can simplify complex workflows in Angular applications.
Preparation: Installing Angular CLI and Creating the App
Before diving into RxJS and Observables in Angular, make sure you have the Angular CLI installed. If you haven't already, install it globally using the following command:
npm install -g @angular/cli
Next, create a new Angular application:
ng new angular-observable-rxjs
During the setup, Angular CLI will prompt you to:
-
Add Angular routing: Yes
-
Choose a stylesheet format: CSS (or any of your choice)
After the app is created, navigate to the project folder:
cd angular-rxjs-observable
Then, run the development server to confirm everything is working:
ng serve --open
Visit http://localhost:4200
in your browser, you should see the default Angular welcome page.
Now you’re ready to implement RxJS and Observable examples in your Angular app.
What is an Observable?
An Observable is a key part of RxJS (Reactive Extensions for JavaScript), which is the reactive programming library Angular uses for handling asynchronous operations.
In simple terms, an Observable is a data stream that can emit multiple values over time. You can subscribe to an Observable to receive these values and react to them as they occur, similar to how you’d respond to events like button clicks or API responses.
Think of it as a more powerful and flexible alternative to Promises, especially useful when:
-
You need to handle multiple values over time
-
You want to cancel a subscription (e.g., unsubscribe when a component is destroyed)
-
You need to compose multiple async operations using operators
Observable vs Promise
Feature | Observable | Promise |
---|---|---|
Emits multiple values | ✅ Yes | ❌ No (resolves once) |
Lazy execution | ✅ Yes (only runs on subscription) | ❌ No (executes immediately) |
Cancellable | ✅ Yes | ❌ No |
Composable with operators | ✅ Yes (via RxJS operators) | ⚠️ Limited |
In Angular, you’ll most commonly encounter Observables when:
-
Making HTTP requests with
HttpClient
-
Listening to form changes via
FormControl.valueChanges
-
Subscribing to route parameters or query parameters
-
Managing state and data flow in services
Next, let’s look at how to create and subscribe to Observables in your Angular app.
Creating and Subscribing to Observables in Angular
To work with Observables in Angular, you’ll typically either create your own Observable using RxJS or consume one from Angular’s built-in APIs, like HttpClient
, FormControl
, or ActivatedRoute
.
Creating an Observable
You can create a basic Observable using RxJS's Observable
constructor or helper functions like of()
, from()
, or interval()
.
Here’s an example using of(), in app.component.ts
:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { of } from 'rxjs';
const fruits$ = of('Apple', 'Banana', 'Mango');
fruits$.subscribe({
next: fruit => console.log(fruit),
complete: () => console.log('All fruits delivered!')
});
@Component({
selector: 'app-root',
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
title = 'angular-observable-rxjs';
}
This will output:
Apple
Banana
Mango
All fruits delivered!
Subscribing to an Observable
Subscribing is how you start listening for data from an Observable. Subscriptions typically include:
-
next
: handle emitted values -
error
: handle errors -
complete
: handle completion
Here’s an example of using interval()
to emit numbers every second:
import { interval } from 'rxjs';
const counter$ = interval(1000);
const subscription = counter$.subscribe(value => {
console.log(`Timer: ${value}`);
});
// Optionally unsubscribe after 5 seconds
setTimeout(() => {
subscription.unsubscribe();
console.log('Unsubscribed');
}, 5000);
This will print:
Timer: 0
Timer: 1
Timer: 2
Timer: 3
Timer: 4
Unsubscribed
Using Observables in Angular Components
Here's a practical example using an Observable in a component with HttpClient
:
import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
@Component({
selector: 'app-data-list',
imports: [],
templateUrl: './data-list.component.html',
styleUrl: './data-list.component.scss'
})
export class DataListComponent {
items: any[] | undefined;
constructor(private readonly http: HttpClient) {
this.http.get<any[]>('https://api.example.com/items')
.subscribe(data => this.items = data);
}
}
This makes an HTTP GET request and assigns the returned data to the items
property.
Using Observable Operators
RxJS operators are powerful tools that let you transform, filter, and combine data streams in a functional style. Angular developers commonly use operators to manipulate data before it reaches the component.
Operators are used with the pipe()
method, which allows you to chain multiple operators together.
Commonly Used Operators
map()
Transforms the emitted values from the source Observable.
import { of } from 'rxjs';
import { map } from rxjs/operators';
of(1, 2, 3)
.pipe(map(value => value * 2))
.subscribe(result => console.log(result)); // Output: 2, 4, 6
filter()
Filters the values emitted by an Observable based on a condition.
import { of } from 'rxjs';
import { filter } from 'rxjs/operators';
of(1, 2, 3, 4, 5)
.pipe(filter(value => value % 2 === 0))
.subscribe(result => console.log(result)); // Output: 2, 4
tap()
Used for side effects (like logging) without affecting the stream.
import { of } from 'rxjs';
import { tap } from 'rxjs/operators';
of('A', 'B', 'C')
.pipe(tap(value => console.log(`Processing: ${value}`)))
.subscribe();
switchMap()
Switches to a new Observable on each emission, canceling the previous one. Often used with HTTP requests.
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, switchMap } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-search',
template: `<input [formControl]="searchControl" placeholder="Search">`
})
export class SearchComponent {
searchControl = new FormControl();
constructor(private http: HttpClient) {
this.searchControl.valueChanges
.pipe(
debounceTime(300),
switchMap(query => this.http.get(`https://api.example.com/search?q=${query}`))
)
.subscribe(results => console.log(results));
}
}
Chaining Operators
You can chain multiple operators to create powerful data pipelines:
import { fromEvent } from 'rxjs';
import { map, filter, debounceTime } from 'rxjs/operators';
fromEvent(document, 'click')
.pipe(
debounceTime(200),
map((event: any) => event.clientX),
filter(x => x > 300)
)
.subscribe(x => console.log(`Click at X: ${x}`));
Handling HTTP Requests with Observables
Angular's HttpClient
Service uses Observables under the hood, making it easy to work with asynchronous data from REST APIs. When you send an HTTP request, Angular returns an Observable, allowing you to subscribe and handle the response.
Importing HttpClientModule
Before using HttpClient
, import HttpClientModule
in your app.config.ts
(or app.module.ts
If you're still using modules):
import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [provideHttpClient()],
};
Making a GET Request
Use the HttpClient.get()
method to retrieve data from a REST API.
import { Component, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-posts',
standalone: true,
template: `
<h2>Posts</h2>
<ul>
<li *ngFor="let post of posts">{{ post.title }}</li>
</ul>
`
})
export class PostsComponent {
private http = inject(HttpClient);
posts: any[] = [];
ngOnInit() {
this.http.get<any[]>('https://jsonplaceholder.typicode.com/posts')
.subscribe(data => this.posts = data);
}
}
Handling Errors
Use the catchError
operator to gracefully handle HTTP errors.
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';
this.http.get<any[]>('https://jsonplaceholder.typicode.com/posts')
.pipe(
catchError(error => {
console.error('Error fetching posts', error);
return of([]);
})
)
.subscribe(data => this.posts = data);
Posting Data
You can also send data using post()
, put()
, or delete()
methods:
this.http.post('https://jsonplaceholder.typicode.com/posts', {
title: 'New Post',
body: 'This is the content.',
userId: 1
}).subscribe(response => console.log(response));
Subjects and Multicasting Observables
While standard Observables are unicast (each subscription receives a separate execution), Subjects are multicast — meaning they allow multiple subscribers to share the same Observable execution. Subjects are both an Observable
and an Observer
, making them powerful for event sharing and state broadcasting.
Creating a Subject
To create a Subject, import it from rxjs
:
import { Subject } from 'rxjs';
const subject = new Subject<number>();
Emitting Values
You can manually emit values using subject.next(value)
:
subject.next(1);
subject.next(2);
Subscribing to a Subject
Multiple subscribers will receive the same values emitted from the Subject:
subject.subscribe(value => console.log('Subscriber A:', value));
subject.subscribe(value => console.log('Subscriber B:', value));
subject.next(100);
// Output:
// Subscriber A: 100
// Subscriber B: 100
Use Case: Component Communication
Subjects are often used for cross-component communication or sharing data streams between services and components.
Example: Shared Service Using Subject
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class MessageService {
private messageSubject = new Subject<string>();
message$ = this.messageSubject.asObservable();
sendMessage(message: string) {
this.messageSubject.next(message);
}
}
Sending and Receiving Messages in Components
Sender Component:
constructor(private messageService: MessageService) {}
send() {
this.messageService.sendMessage('Hello from Sender!');
}
Receiver Component:
constructor(private messageService: MessageService) {}
ngOnInit() {
this.messageService.message$.subscribe(msg => {
console.log('Received:', msg);
});
}
Conclusion
In this tutorial, we explored how to effectively use Observables and RxJS in modern Angular applications. You learned the fundamentals of Observables, how to create and subscribe to them, use powerful RxJS operators, handle HTTP requests, and utilize Subjects for multicasting and component communication.
Mastering Observables is essential for building reactive and maintainable Angular apps. With RxJS, you can streamline asynchronous operations, improve code clarity, and enhance the user experience through responsive UI updates.
As Angular continues to evolve, reactive programming remains a core concept. Keep experimenting with more advanced operators like mergeMap
, switchMap
, concatMap
, and combineLatest
to unlock even more powerful patterns in your projects.
You can get the full source code from our GitHub.
If you don’t want to waste your time designing your front-end or your budget to spend by hiring a web designer, then Angular Templates is the best place to go. So, speed up your front-end web development with premium Angular templates. Choose your template for your front-end project here.
That's just the basics. If you need more deep learning about MEAN Stack, Angular, and Node.js, you can take the following cheap course:
- Master en JavaScript: Aprender JS, jQuery, Angular 8, NodeJS
- Angular 8 - Complete Essential Guide
- Learn Angular 8 by creating a simple Full Stack Web App
- Angular 5 Bootcamp FastTrack
- Angular 6 - Soft & Sweet
- Angular 6 with TypeScript
Thanks!