RxJS: How to Observe an Object
June 14, 2018 • 3 minute read
A while ago, John Lindquist published a package named rx-handler
. With it, you can create event handler functions that are also observables.
When it was published, I noticed a few queries about whether something similar could be done with Angular’s Input
properties — so that they, too, could be treated as observables.
I was thinking about this last night and I’ve published a small package that can be used to observe the properties and methods of an arbitrary object: rxjs-observe
.
How does it work?
Let’s look at an example:
import { observe } from "rxjs-observe";
const instance = { name: "Alice" };
const { observables, proxy } = observe(instance);
observables.name.subscribe((value) => console.log(name));
proxy.name = "Bob";
When an object instance
is passed to the observe
function, it returns an observables
object — containing an observable source for the name
property — and a proxy
instance.
When the name
property of the proxy
is assigned, the observable source emits the assigned value — which is written to the console.
The TypeScript declaration for observe
ensures that the observables
object is strongly-typed — containing appropriately-typed observables for each property and method on the instance
.
Internally, a Proxy
is created for the instance. The proxy is used to intercept property assignments and method calls. A proxy is used for observables
, too, so that an observable source is only created for a property or method if the source is actually used.
How could it be used with Angular?
With Angular’s component API, you create a component class and Angular sets its properties and calls its methods. You could use rxjs-observe
to convert this to an observable API.
Let’s look at an example component:
import { Component, Input, OnInit, OnDestroy } from "@angular/core";
import {
debounceTime,
distinctUntilChanged,
switchMapTo,
takeUntil
} from "rxjs/operators";
import { observe } from "rxjs-observe";
@Component({
selector: "some-component",
template: "<span>Some useless component that writes to the console</span>"
})
class SomeComponent implements OnInit, OnDestroy {
@Input() public name: string;
constructor() {
const { observables, proxy } = observe(this as SomeComponent);
observables.ngOnInit
.pipe(
switchMapTo(observables.name),
debounceTime(400),
distinctUntilChanged(),
takeUntil(observables.ngOnDestroy)
)
.subscribe((value) => console.log(value));
return proxy;
}
ngOnInit() {}
ngOnDestroy() {}
}
In the component, observables
will contain observable sources for:
- calls to the
ngOnInit
method; - calls to the
ngOnDestroy
method; and - assignments to the
name
property.
Using those observables, the component: composes another observable that debounces changes to the name
input; writes said changes to the console; and unsubscribes when the component is destroyed.
Writing the name
to the console isn’t particularly useful, but it does show how an observable API could be used within an Angular component.
Should it be used with Angular?
rxjs-observe
was fun to write, but is it something you should really be using in an Angular component?
I dunno; it’s definitely unconventional. And it requires TypeScript 2.8 or later.
So maybe. Or maybe not, but … YOLO.