Introduction to the
Event handling is a common implementation when building TypeScript applications.
Registering an event and performing actions when the event is triggered requires an Observable pattern that provides a one-way flow of events, making debugging and error handling easier.
In this article, we’ll explore Observables and how it can be used to handle event-driven data, as well as error and asynchronous data.
-
- Introduction to observable variables
- Observable operators
- Transform an array with An Observable
- Combine multiple operators for event flow transformation
- Handle errors with Observables
Introduction to observable variables
In general, the fun, but sometimes nerve-racking, part of JavaScript is handling events.
Consider a live chat application like Facebook, which has multiple events occurring in a particular time frame. A user might be typing some text into his news feed and receiving notifications and messages from other friends, in no particular order. It is now the application’s responsibility to handle these events. This is what Observables do.
An Observable is a collection of input values processed using array methods such as Map, Reduce,filter, and so on. It is very convenient for handling asynchronous operations, such as making HTTP requests, user input events, and so on.
The observable pattern is a design pattern for registering an event and implementing a process when the event is triggered. One of the most powerful and popular JavaScript libraries dedicated to handling events is the Reactive Extensions for JavaScript library, also known as RxJS.
To get started, we need to make sure we have the RxJS library installed. So, we install the library with this command.
npm install rxjs
Copy the code
Already, the library has all TypeScript declaration files, so it doesn’t need a separate installation.
To create an Observable, we need the RxJS Observable type and the of method, as shown below.
import { of, Observable } from "rxjs";
const emitter : Observable<string> = of("Sam", "Ray", "Thomas");
Copy the code
In the above clip, we import the of function and Observable type from the RxJS library, and create an Observable from the strings “Sam”,”Ray”, and “Thomas”.
Next, we subscribe to an Observable, as shown below.
emitter.subscribe((value: string) => {
console.log(`Name: ${value}`)
})
Copy the code
In the above snippet, we register an Observer by calling the SUBSCRIBE method on the Emitter variable. Since the Emitter variable is an Observable, we can automatically access the Subscribe method. The subscribe method takes as an argument a function that is called once for each value emitted by the Observable.
The above code outputs the following Observable flow.
Name: Sam
Name: Ray
Name: Thomas
Copy the code
Observable operators
Pipeable Operators and Creation Operators are two Operators in RxJS.
Pipeable Operators are methods that take an Observable as input and return another Observable. They can be pipeded to Observables using the syntax observableInstance.pipe(operator()). It includes [filter (…)” (https://rxjs.dev/api/operators/filter) and [mergeMap (…)” (https://rxjs.dev/api/operators/mergeMap).
Creation Operators are the Operators that create a new Observable when called.
The create operator includes the following.
[from](https://rxjs.dev/api/index/function/from)
[interval](https://rxjs.dev/api/index/function/interval)
[of](https://rxjs.dev/api/index/function/of)
[range](https://rxjs.dev/api/index/function/range)
[throwError](https://rxjs.dev/api/index/function/throwError)
You can refer to the official RxJS documentation for a complete list of operators.
Transform an array with An Observable
The RxJSfrom method allows you to convert the data in an array. It takes an array as input and converts each data in the array into an Observable.
Let’s look at the following code snippet.
const transformArray: Observable<number> = from([1, 2, 3, 4]);
transformArray.subscribe((value: number) => {
console.log(`value: ${value}`);
});
Copy the code
The code snippet above uses the FROM method to convert the value in the array to Observable, and then calls subscribe on the transformArray variable. The subscribe method accepts a function that executes on every value emitted by an Observable.
Combine multiple operators for event flow transformation
RxJS gives us the PIPE method, which allows you to combine multiple operator methods for complex transformations of event flows.
Let’s consider the following code.
const emitter = of(4, 9, 16, 25)
const mapValue = emitter.pipe(
map((value: number) => {
return Math.sqrt(value)
}),
map((value: number) => {
return `square root: ${value}`
})
)
mapValue.subscribe((value: string) => {
console.log(`string value emitted ${value}`)
})
Copy the code
Here, RxJS’s of method is used to create an Observable from the numbers 4,9,16, and 25. The PIPE method receives two map methods. The first map method returns the square root of the input value, and the second map method converts the output of the first map method into a string.
Finally, the SUBSCRIBE method is called on each emitted Observable.
Running the code above produces the following output.
string value emitted square root: 2
string value emitted square root: 3
string value emitted square root: 4
string value emitted square root: 5
Copy the code
Observed errors
Handling exceptions in the Observable flow requires a well-structured mechanism to catch them.
Let’s consider the following code snippet.
interface IName { value: string; } interface IObj { name? : IName; } const emitObj: Observable<IObj> = of( { name: { value: "Bob" } }, {}, { name: { value: "Sam" } } );Copy the code
In the code snippet above, we create IName with a property of value and type string. At the same time, we create IObj with an optional attribute name of type IName. Then we create the emitObj observer, which emits three values.
Now let’s consider the following Observable flow.
const returnName = emitObj.pipe(
map((value: IObj) => {
return value.name!.value;
})
);
returnName.subscribe((value: string) => {
console.log(`name: ${value} `)
});
Copy the code
In the above snippet, we create an Observable streamreturnName that returns the name.value property for the input stream value. We then subscribe to the stream and log the received values to the console.
When we run the fragment, we get the following output.
name: Bob
TypeError: Cannot read property 'value' of undefined
Copy the code
This error occurs because the second value emitted in our Observable stream does not have a name attribute, resulting in an undefined value.
To resolve this error, we can create an error handler for the SUBSCRIBE function in our Observable stream.
Let’s consider the following case.
returnName.subscribe( // called for each observable value (value: string| null) => { console.log(`name: ${value} `); }, // called if an error occurs (error: unknown) => { console.log(`error : ${error}`); }, // called when execution is done () => { console.log(`done`); });Copy the code
Here, we provide three functions as arguments to the SUBSCRIBE method. Each value emitted by the Observable stream calls the first function, the second function if an error occurs, and the last function is called after the SUBSCRIBE method completes execution.
When we run the fragment, we get the following output.
name: Bob
TypeError: Cannot read property 'value' of undefined
Copy the code
What happens here is that the first value emitted by the Observable stream is handled by the first function supplied to the Subscribe method. The second value emitted by the Observable stream causes an error that is caught by the error function provided to the SUBSCRIBE method. The last function is not called due to an error in the Observable stream.
catchError
Another way to handle Observable errors is to use the catchError operator in the Observable stream itself, so we can catch them early enough.
Let’s consider the following code snippet.
const returnName = emitObj.pipe( map((value: IObj) => { return value! .name! .value; }), catchError((error: unknown) => { console.log(`stream caught : ${error}`); return of(null); }));Copy the code
Here, we add a catchError operator to the Observable stream. In this function, we log error messages to the console and return an Observable value: null. With this implementation, the last Observable is triggered even if an error occurs in the Observable.
When we run the fragment, we get the following output.
Name: Bob
stream caught : TypeError: Cannot read property 'value' of undefined
received null
done
Copy the code
What happens here is that the map method generates an error on the second value of the Observable stream, and our catchError function is called with the following error: “cannot read undefined property ‘value’.” At the same time, the last method is fired.
If an error occurs in the Observable stream, the stream stops emitting values. This is why the emitObj Observable stream does not emit the value of its last parameter.
conclusion
In this article, we’ve looked at data transformations using Observable streams and how to implement them. In addition, we discussed error handling for Observables and provided strategies for implementing error handling when using Observable flows.
Hope you find this post informative and helpful. You can also check out the official RxJS documentation to learn more about the RxJS Observable module. In addition, I recommend Brian Troncone’s Learnrxjs.io for a more intuitive explanation of RxJS concepts.
The postUsing Observables to transform data in TypeScriptappeared first onLogRocket Blog.