Using Observable.create for fine-grained control

Shane Osbourne
InstructorShane Osbourne

Share this video with your friends

Send Tweet
Published 7 years ago
Updated 4 years ago

Sometimes, the helper methods that RxJS ships with such as fromEvent, fromPromise etc don't always provide the exact values you want & you end up having to do extra work to force them into the shape you require. For more fine-grained control you can use Observable.create which allows you to project only the values which matter to you.

[00:01] RXJS ships with many helper methods that allow you to turn various data sources into observable sequences. So there are things such as fromPromise, fromNodeCallback, fromCallback, fromItself which can accept arrays or array-like objects, and various others. Sometimes though, the values that are projected from these helpers are not always exactly what you want.

[00:26] In this example where we're doing some event delegation, we're using the fromEvent helper on a single DOM node which is the wrapper, we add a click event, and then in this selector function here, we return the closest DOM element that matches this selector here. So clicking on this wrapper does nothing, but clicking on the buttons gives us back the buttons, all achieved with a single event listener.

[00:47] But notice this line here, we're having to filter out null values. That's because the closest method available to DOM elements will return null if a match is not found, and because the fromEvent helper always uses the value that is returned from the selector function, we have no way of avoiding that null value making it into the sequence. That's why we call filter here, but behind the scenes RX has to create another observable just for this filter.

[01:17] So in some cases this is not idea, and if you want a bit more fine-grained control, you can use create instead. So let's keep this around for reference, but we'll re-implement it using create. We'll call create and pass along a function which is given an observer. Using this observer, we can decide exactly which values are projected into the sequence. Looking at our previous implementation, we need to get a DOM element. We can say wrapper, then we'll need to create a handle, this is what will receive the browser click event.

[01:58] But whereas before we always returned the value of calling the closet method on the event target, here we don't have to do that. We can first check for a match, then if it's not null we can project this value into the sequence. We do that by calling onNext and giving the onMatch. So we have the wrapper, we have the handle, we just need to add the event listener to the wrapper. We'll use the native addEventListener, pass along the event name that's given here, the handle, and then false for useCapture.

[02:42] So now this observable knows how to add the event listener to the DOM element, we need teach it how to remove it once complete. We can do that by returning a function. This function will be called once this observable is disposed of, so we can call wrapper RemoveEventListener event name, and handle. Now if we call this delegate function as we did before, hit save, we get the exact same result without the additional filter observable that would be created in the first example.

[03:20] So to recap, sometimes the values that are projected from the helpers that RXJS ships with, are not exactly what you want, and you end up having to do a little bit of extra work to force them into the shape that want. If you find yourself doing this, you can always use create instead.

[03:37] Create is the factory function that creates an anonymous observable for you, gives you the opportunity to only project the values that you want. You end up having to do a bit more manual work, such as adding event listeners and removing them finished with, but the result will be greater performance and greater control.