Using RxJS in Angular and Its Advantages
Explore how RxJS serves as a core part of Angular's reactive programming paradigm. Understand its use in handling HTTP requests, routing events, reactive forms, event emitters, and the async pipe to manage asynchronous streams efficiently within Angular applications.
RxJS is a first-class citizen in Angular. It’s part of the Angular ecosystem and used in many features to handle asynchronous operations. Primarily, these include the following:
- HTTP client module
- Router module
- Reactive forms
- Event emitter
- Async pipe
We’ll discuss each of the following concepts in the subsequent subsections.
Note: Further details about the features mentioned earlier can be found in the Angular documentation.
The HTTP client module
Angular provides the HTTP client API to communicate with the server over the HTTP. The HttpClient service is based on observables to manage all transactions. This means that the result of calling the API methods (such as GET, PATCH, POST, or PUT) is observable.
In the following code snippet, we have an example of an Angular service that injects the HttpClient service and fetches data from the server using the HttpClient.get() method.
The following is the content of the environment.ts file, where we define the basePath property of our backend:
The getRecipes() method or, to be more accurate, the call to this.http.get<Recipe('${BASE_PATH}/recipes/search') returns an observable that we should subscribe to in order to issue the GET request to the server. Please note that this is an example of an HTTP transaction, and it’s the same for all of the other HTTP methods available in the API (such as POST, PUT, and PATCH).
If you’re familiar with promise-based HTTP APIs, you might be wondering, in this case, what the advantages of using observables are. Well, there are a lot of advantages, but here are the most important ones:
- Observables are cancellable, so we can cancel the HTTP request whenever we want by calling the unsubscribe method.
- Also, we can retry HTTP requests when an error occurs or an exception is thrown.
- The server’s response cannot be mutated by observables, although this can be the
case when chaining
then()to promises.
The router module
The router module, which is available in the @angular/router package, uses observables in router events and activated routes.
Router events
The router exposes events as observables. The router events allow us to intercept the navigation life cycle. The following list shows the sequence of router events:
NavigationStartRouteConfigLoadStartRouteConfigLoadEndRoutesRecognizedGuardsCheckStartChildActivationStartActivationStartGuardsCheckEnd
ResolveStartResolveEndActivationEndChildActivationEndNavigationEndNavigationCancelNavigationErrorScroll
To intercept all the events that the router goes through, first we should inject the Router service, which provides navigation and URL manipulation capabilities. Then we subscribe to the events observable available in the Router object and filter the events of the RouterEvent type using the rxjs filter operator.
This is an example of an Angular service that injects the Router object in the constructor, subscribes to the router events, and just traces the event ID and path in the console. However, note that we can also introduce pretty much any specific behavior.
We can filter any specific event by inputting the target type. The following code example only filters the NavigationStart event and traces the event ID and path inside the console. However, we can also introduce pretty much any specific behavior.
The majority of Angular applications have a routing mechanism. The router events change frequently over time, and it makes sense to listen to changes to execute the side effects. That’s why observables are a flexible way in which to handle those streams.
The activated route
The ActivatedRoute class is a router service that we can inject into the components to retrieve information about a route’s path and parameters. Many properties are based on observables. Here, we’ll find the contract (refers to the exposed methods and properties) of the activated route class:
The url, params, queryParams, fragment, data, paramMap, and queryParamMap properties are represented as observables and described in the following list:
url: This is an observable that holds the URL of the active route.params: This is an observable that holds the parameters of the active route.queryParams: This is an observable that holds the query parameters shared by all the routes.fragment: This is an observable that holds the URL fragment shared by all the routes.data: This is an observable that holds the static and resolved data of the active route.paramMap: This is an observable that holds a map of the required parameters and the optional parameters of the active route.queryParamMap: This is an observable that holds a map of the query parameters available to all the routes.
All these parameters might change over time. Routes might share parameters, and the parameters might have dynamic values, and it makes perfect sense to listen to those changes to register side effects or update the list of parameters.
Here’s an example of an Angular component that injects the ActivatedRoute class in the constructor and subscribes, in the ngOnInit() method, to the following:
- The
urlproperty ofactivatedRoute, logging the URL in the console. - The
queryParamsproperty ofactivatedRoutein order to retrieve the parametercriteriaand store it in a local property, namedcriteria.
Reactive forms
Reactive forms available under the @angular/forms package are based on observables to track form control changes. Here’s the contract of the FormControl class in Angular:
The FormControl properties of valueChanges and statusChanges are represented as observables that trigger change events. Subscribing to a FormControl value change is a way of triggering application logic within the component class.
Here’s an example of an Angular component that subscribes to the valueChanges of a FormControl property called rating and simply traces the value through the console.log(value). In this way, each time, we’ll get the changed value as an output.
The event emitter
The event emitter, which is part of the @angular/core package, is used to emit data from a child component to a parent component through the @Output() decorator. The EventEmitter class extends the RxJS subject and registers handlers for events emitted by this instance:
This is what happens under the hood when we create an event emitter and emit a value.
The following is an example of an Angular component that emits the updated value of a recipe rating:
The async pipe
Here, AsyncPipe automatically subscribes to an observable when used in a component’s template and emits the latest value each time. This avoids subscribing logic in the component and helps with binding and updating the asynchronous stream data in the template. In this example, we’re using an async pipe inside ngIf. This div tag will only be rendered when the data$ variable emits something.
We’ll cover the advantages and usage of async pipes in Fetching Data As Streams.
Note: In the previous code snippets, the subscription to the observables was done explicitly for demonstration purposes. In a real-world example, we should include the unsubscription logic if we use an explicit subscription.