How can Observable class be extended by applying built-in RxJS operators to it?
I would like to do something like this:
class TruthyObservable extends Observable {
constructor(subscriber) {
super(subscriber);
return this.filter(x => x);
}
}
class TruthyMappedObservable extends TruthyObservable {
constructor(subscriber) {
super(subscriber);
return this.map(x => `'${x}'`);
}
}
Can this be done without constructor return?
This pretty much depends on what you want to do but let's say you want to make a TruthyObservable that behaves very much like the default Observable.create(...) but passes only even numbers:
import { Observable, Observer, Subscriber, Subject, Subscription } from 'rxjs';
import 'rxjs/add/operator/filter';
class TruthyObservable<T> extends Observable<T> {
constructor(subscribe?: <R>(this: Observable<T>, subscriber: Subscriber<R>) => any) {
if (subscribe) {
let oldSubscribe = subscribe;
subscribe = (obs: Subscriber<any>) => {
obs = this.appendOperators(obs);
return oldSubscribe.call(this, obs);
};
}
super(subscribe);
}
private appendOperators(obs: Subscriber<any>) {
let subject = new Subject();
subject
.filter((val: number) => val % 2 == 0)
.subscribe(obs);
return new Subscriber(subject);
}
}
let o = new TruthyObservable<number>((obs: Observer<number>) => {
obs.next(3);
obs.next(6);
obs.next(7);
obs.next(8);
});
o.subscribe(val => console.log(val));
This prints to console:
6
8
See live demo: https://jsbin.com/recuto/3/edit?js,console
Usually classes inheriting Observable override the _subscribe() method that actually makes the subscription internally but in ours case we want to use the callback where we can emit values by ourselves (since this Observable doesn't emit anything itself). Method _subscribe() is overshadowed by _subscribe property if it exists so we wouldn't be able to append any operators to it if we just overrode this method. That's why I wrap _subscribe in the constructor with another function and then pass all values through a Subject chained with filter() in appendOperators() method. Note that I replaced the original Observer with the Subject at obs = this.appendOperators(obs).
At the end when I call eg. obs.next(3); I'm in fact pushing values to the Subject that filters them and passes them to the original Observer.
I think you can get what you need with custom operator:
Observable.prototype.truthy = function truthy() {
return this.filter(x => x);
}
Related
I have a function _populateData that creates a new list of properties from another list of properties.
There is an observable getOwnerForProperty that returns the owner's value.
//Get single owner observable
public getOwnerForProperty(prop: any){
return this._manageOwnerService._getOwnersOfProperty(prop).pipe(map(o => o[0]))
How can I call the observable from within the .map() function to obtain the observable's value and attach it to the new object as seen below?
In my opinion, it would not be a good idea to subscribe getOwnerForProperty function in the .map(). Please advise on the best way to approach this following best practices.
/**
* Returns the active properties data.
*
* #param props - The property list.
* #returns An array of properties
*/
private _populateData(props: Property[]) {
return
const populated = props
.filter(prop => !prop.isArchived)
.map((p: Property) => {
// refactoring here
this.getOwnerForProperty(p).pipe(
map((owner: Owner) => {
const obj = {
propertyName: p.info.name.toUpperCase(),
owner: owner.name,
createdOn: p.createdOn ? __FormatDateFromStorage(p.createdOn) : ''
}
return obj;
})
)
}
)
return populated;
}
}
It's not entirely clear from your question what exactly you are trying to achieve, but here is my solution, so you will hopefully get the idea:
filter for the properties you want to "enrich".
use forkJoin to create an array of observables and wait for all of them to complete.
map each property to the observable you want to wait for.
map the result of the observable to the initial property and enrich it with the owner object.
forkJoin returns an observable which will basically emit a single array of enriched objects and complete. If you wish to await this, you can wrap this in lastValueFrom operator, like await lastValueFrom(forkJoin(...))
function _populateData(props: Property[]) {
const propertiesToPopulate = props.filter((prop) => !prop.isArchived);
forkJoin(
propertiesToPopulate.map((p: Property) => {
return getOwnerForProperty(p).pipe(
map((owner) => ({
...p,
owner,
}))
);
})
);
}
I am sure a waterfall observable isn't a thing, but it best describes what I want to do.
Code
Given is a map of paths and behavior objects that represent their content.
const pathmap = new Map<string, BehaviorSubject<string>>();
pathmap.set("foo.txt", new BehaviorSubject<string>("file is empty"));
pathmap.set("bar.txt", new BehaviorSubject<string>("file is empty"));
Also given is a BehaviourSubject that contains the active path.
const activePath = new BehaviorSubject<string | null>(null);
...
activePath.next('bar.txt');
What I need:
I want to create a single chained Observable that triggers an event when:
A) The active file path got changed.
B) The content of a file got changed.
What I have so far:
https://codesandbox.io/s/bold-dream-3hfjdy?file=/src/index.ts
function getFileContentObservable(): Observable<string> {
return currentFile.asObservable()
.pipe(map((path: string) => pathmap.get(path).asObservable()))
.pipe(map((content: string) => `<h1>${content}</h1>`));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Type 'string' is not assignable to type 'Observable<string>'
}
How would I chain this?
Try switchMap Operator.
function getFileContentObservable(): Observable<string> {
return currentFile.asObservable().pipe(
filter(Boolean),
switchMap((path: string) => pathmap.get(path).asObservable()),
map((content: string) => `<h1>${content}</h1>`)
);
}
I am converting some Q-promise based typescript code to ES6-promises.
At a certain point, I used Q.defer and in my migration I just rewritten defer as an ES6 promise like explained in this comment:
https://stackoverflow.com/a/49825137/13116953
I was trying to get rid of this defer approach, if possible, and was looking for alternative solutions.
Reasons of my question are:
Deferred promises are considered an anti-pattern, in general
Want to know if this scenario is one of the few where deferred are really the only way
Here's my scenario:
// app start, this is my low level API
init() {
commService.subscribe("myRecordId", {
onRecordAdd: this.onAdd
});
}
...
private _myRecord: MyRecordObj | null = null;
private _recordReceivedDef = newDeferred(); // was Q.defer()
// callback will be called when record is received
private readonly onAdd = (recordObj: MyRecordObj) => {
this._myRecord = recordObj;
this._recordReceivedDef.resolve(recordObj);
}
...
// here's my async code that requires the deferred
public readonly doStuff = async () => {
// ...
const myRec = await this._recordReceivedDef.promise;
// use myRef
}
My question is: is there a way I can get rid of this defer?
I was thinking of something that resolves when _myRecord changes, but have no idea how to do it.
Side note:
I use MobX in other parts of our app, thus having
await when(() => this._myRecord); // of course _myRecord must be #observable
would be handy, but unfortunately I cannot use MobX in this particular piece of code.
Any help is much appreciated.
Thanks a lot!
Assuming init is called before doStuff, the proper way would be
init() {
this._myRecordPromise = new Promise((resolve, reject) => {
commService.subscribe("myRecordId", {
onRecordAdd: (recordObj: MyRecordObj) => {
// callback will be called when record is received
resolve(this._myRecord = recordObj);
}
});
});
}
…
private _myRecord: MyRecordObj | null = null;
private _myRecordPromise: Promise<MyRecordObj>;
…
public readonly doStuff = async () => {
…
const myRec = await this._myRecordPromise;
// use myRef
}
You might even drop the _myRecord completely and keep only the _myRecordPromise.
However, you might want to consider not constructing your instance at all before the record is received, see Is it bad practice to have a constructor function return a Promise?.
If init is called at some arbitrary time, you will need some kind of defer pattern, but you don't need newDeferred() for that. Just write
init() {
commService.subscribe("myRecordId", {
onRecordAdd: this.onAdd
});
}
…
private _myRecordPromise: Promise<MyRecordObj> = new Promise(resolve => {
this.onAdd = resolve;
});
private readonly onAdd: (recordObj: MyRecordObj) => void;
For people who might be interested, there is also another solution that uses an event emitter approach.
Suppose you have a class EventEmitter that implements the following interface:
// pseudo code
type UnsubscribeFn = () => void;
interface IEventEmitter<T> {
/** Fires an event */
emit(args: T): void;
/** Subscribes to emissions of this event and provides an unsubscribe function */
subscribe((args: T) => void): UnsubscribeFn;
}
you can provide your clients with a whenAdded function like below
// pseudo code
class Subscriber {
readonly onAddEmitter = new EventEmitter<MyRecordObj>();
// app start
init() {
commService.subscribe("myRecordId", {
onRecordAdd: this.onAdd
});
}
onAdd = (rec: MyRecordObj) => {
// do some stuff
onAddEmitter.emit(rec);
}
whenAdded(): Promise<MyRecordObj> {
return new Promise((res) => {
const unsub = onAddEmitter.subscribe((record: MyRecordObj) => {
unsub();
res(record);
});
});
}
}
The goals achieved are:
Get rid of Q promises in favour of ES6 ones
Adapt the commService event API to a promise-based one
I am a relative beginner in Angular, and I am struggling to understand some source I am reading from the ng-bootstrap project. The source code can be found here.
I am very confused by the code in ngOnInit:
ngOnInit(): void {
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
const results$ = letProto.call(inputValues$, this.ngbTypeahead);
const processedResults$ = _do.call(results$, () => {
if (!this.editable) {
this._onChange(undefined);
}
});
const userInput$ = switchMap.call(this._resubscribeTypeahead, () => processedResults$);
this._subscription = this._subscribeToUserInput(userInput$);
}
What is the point of calling .call(...) on these Observable functions? What kind of behaviour is this trying to achieve? Is this a normal pattern?
I've done a lot of reading/watching about Observables (no pun intended) as part of my Angular education but I have never come across anything like this. Any explanation would be appreciated
My personal opinion is that they were using this for RxJS prior 5.5 which introduced lettable operators. The same style is used internally by Angular. For example: https://github.com/angular/angular/blob/master/packages/router/src/router_preloader.ts#L91.
The reason for this is that by default they would have to patch the Observable class with rxjs/add/operators/XXX. The disadvantage of this is that some 3rd party library is modifying a global object that might unexpectedly cause problems somewhere else in your app. See https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md#why.
You can see at the beginning of the file that they import each operator separately https://github.com/ng-bootstrap/ng-bootstrap/blob/master/src/typeahead/typeahead.ts#L22-L25.
So by using .call() they can use any operator and still avoid patching the Observable class.
To understand it, first you can have a look at the predefined JavaScript function method "call":
var person = {
firstName:"John",
lastName: "Doe",
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
var myObject = {
firstName:"Mary",
lastName: "Doe",
}
person.fullName.call(myObject); // Will return "Mary Doe"
The reason of calling "call" is to invoke a function in object "person" and pass the context to it "myObject".
Similarly, the reason of this calling "call" below:
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is providing the context "this._valueChanges", but also provide the function to be called base on that context, that is the second parameter, the anonymous function
value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
}
In the example that you're using:
this._valueChanges is the Input Event Observerable
The _do.call is for doing some side affects whenever the event input happens, then it returns a mirrored Observable of the source Observable (the event observable)
UPDATED
Example code: https://plnkr.co/edit/dJNRNI?p=preview
About the do calling:
You can call it on an Observable like this:
const source = Rx.Observable.of(1,2,3,4,5);
const example = source
.do(val => console.log(`BEFORE MAP: ${val}`))
.map(val => val + 10)
.do(val => console.log(`AFTER MAP: ${val}`));
const subscribe = example.subscribe(val => console.log(val));
In this case you don't have to pass the first parameter as the context "Observable".
But when you call it from its own place like you said, you need to pass the first parameter as the "Observable" that you want to call on. That's the different.
as #Fan Cheung mentioned, if you don't want to call it from its own place, you can do it like:
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
I suppose
const inputValues$ = _do.call(this._valueChanges, value => {
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
});
is equivalent to
const inputValues$=this._valueChanges.do(value=>{
this._userInput = value;
if (this.editable) {
this._onChange(value);
}
})
In my opinion it's not an usual pattern(I think it is the same pattern but written in different fashion) for working with observable. _do() in the code is being used as standalone function take a callback as argument and required to be binded to the scope of the source Observable
https://github.com/ReactiveX/rxjs/blob/master/src/operator/do.ts
I'm playing with angular2 alpha 40 with ng2-play starter from pawel.
Examples are in typescript.
I have a service MovieList like this:
export class Movie {
selected: boolean = false
constructor(public name:string, public year:number, public score:number) {}
}
export class MovieListService {
list: Array<Movie>
selectMovie = new EventEmitter();
constructor() {
this.list = [new Movie('Star Wars', 1977, 4.4)];
}
add(m:Movie) {
this.list.push(m);
}
remove(m:Movie) {
for(var i = this.list.length - 1; i >= 0; i--) {
if(this.list[i] === m) {
if(m.selected) this.selectMovie.next();
this.list.splice(i, 1);
}
}
}
select(m:Movie) {
this.list.map((m) => m.selected = false);
m.selected = true;
this.selectMovie.next(m);
}
}
I have a component showing the movies list and make possible to select one by clicking on it, which call select() in the service above.
And I have another component (on the same level, I don't want to use (selectmovie)="select($event)") which subscribe to the movie selection event like this:
#Component({
selector: 'movie-edit',
})
#View({
directives: [NgIf],
template: `
<div class="bloc">
<p *ng-if="currentMovie == null">No movie selected</p>
<p *ng-if="currentMovie != null">Movie edition in progress !</p>
</div>
`
})
export class MovieEditComponent {
currentMovie:Movie
constructor(public movieList: MovieListService) {
this.movieList.selectMovie.toRx().subscribe(this.movieChanged);
setTimeout(() => { this.movieChanged('foo'); }, 4000);
}
movieChanged(f:Movie = null) {
this.currentMovie = f;
console.log(this.currentMovie);
}
}
The event is subscribed using .toRx().subscribe() on the eventEmitter.
movieChanged() is called but nothing happen in the template..
I tried using a timeout() calling the same function and changes are refleted in the template.
The problem seems to be the fact that subscribe expects an Observer or three functions that work as an observer while you are passing a normal function. So in your code I just changed movieChanged to be an Observer instead of a callback function.
movieChanged: Observer = Observer.create(
(f) => { this.currentMovie = f; }, // onNext
(err) => {}, // onError
() => {} // onCompleted
);
See this plnkr for an example. It would have been nice to see a minimal working example of your requirement so my solution would be closer to what you are looking for. But if I understood correctly this should work for you. Instead of a select I just used a button to trigger the change.
Update
You can avoid creating the Òbserver just by passing a function to the subscriber method (clearly there's a difference between passing directly a function and using a class method, don't know really why is different)
this.movieList.selectMovie.toRx().subscribe((m: Movie = null) => {
this.currentMovie = m;
});
Note
EventEmitter is being refactored, so in future releases next will be renamed to emit.
Note 2
Angular2 moved to #reactivex/rxjs but in the plnkr I'm not able to use directly those libs (didn't find any cdn). But you can try in your own project using these libs.
I hope it helps.
The movieChanged function expects the movie object and not the String. Try changing below code
setTimeout(() => { this.movieChanged('foo'); }, 4000);
to
setTimeout(() => { this.movieChanged(new Movie('Troy', 2000 , 8)); }, 4000);