Usage of non-argument variables in rxjs operators - rxjs5

Let's say there is function that returns observable, and the function has an argument
getObservable(arg){
return obs$.pipe(map(data => { // wanna use arg here }));
}
I am probably being paranoid here but it always confuses me if I can use arg inside map's callback and therefore I use combineLatest or withLatestFrom to send arg inside operator which feels redundant, but I am not sure.
getObservable(arg){
return combineLatest(of(arg), obs$).pipe(
map(([arg, data]) => {})
);
}
Could there be any problem if I go with the first way?

Related

Should I use map or switchmap when using angular http module?

I use the following code in an angular app. I used the RxJS map call similar to how array map is used. After reading about RxJS switchmap operator, I'm not sure whether I should use map or switchmap. Should I use switchmap so the observable which is returned from the http call is closed so there is no memory leak?
getPeopleForTypeahead(term: string): Observable<IPersonForTypeahead[]> {
var peopleUrl = `https://localhost:5001/api/peoplesearch?name=${term}`;
return this.http.get<any>(peopleUrl)
.pipe(
map(pl => {
return this.peopleAsFlattened(pl.peopleList).reduce((p, c) => p.concat(c));
}),
catchError(this.handleError('getPeopleForTypeahead', []))
);
}
peopleAsFlattened = (pla: IPeopleList[]) => {
return pla.map(pl => pl.people.map(p => {
return {
id: p.id,
fullName: p.fullNames[0].firstName + " " + p.fullNames[0].lastName
};
}));
}
map and switchMap have completely different purposes:
map - transform the shape of an emission
switchMap - subscribe to an observable and emit its emissions into the stream
map
Use map when you want transform the shape of each emission. Ex: emit the user name property, instead of the entire user object:
userName$: Observable<string> = this.service.getUser(123).pipe(
map(user => user.name)
);
switchMap
Use switchMap when you want to map an emission to another observable and emit its emissions. Ex: You have an observable of some id and want to emit the resource after fetching it:
user$: Observable<User> = this.userId$.pipe(
switchMap(id => this.service.getUser(id)),
);
When user$ is subscribed to, the user returned from service.getUser(id) is emitted (not the userId string).
switchMap is not interchangeable with the map operator, nor vise versa. Although both of them has to do with mapping (as their names suggest), they have two separate use-cases.
In your particular case, the map operator is the way to go.
When to use switchMap?
You can only use switchMap(cb) when you check all these requirements:
Your callback function, cb, passed into switchMap returns an observable, observable$.
If your cb (callback function) does not return an observable, you should look into operators that don't handle higher-level observables, such as filter and map (what you actually needed); not operators that handle higher-level observables such as concatMap and well, switchMap.
You want to execute your cb sequentially before the next operation down the pipeline (after switchMap) executes.
Maybe you want to run logic inside of cb, and optionally get the return value of cb after executing, so that you can pass it down the pipeline for further processing, for example.
When you want to "discard" what will happen to cb's execution and re-execute cb every time the source observable (the thing that trickles down to switchMap(cb)) emits a new value/notification.
Applying what we hopefully learned, we know that your cb:
pl => {
return this.peopleAsFlattened(pl.peopleList).reduce((p, c) => p.concat(c));
}
returns a plain JavaScript array; not an observable. This takes using switchMap out of the question since it violates the first requirement I made up above.
Hopefully that makes sense. :)
We use switchMap when the source observable is a hot observable. In which case you prefer the behaviour that cancel the succeeding observable when source emits.
In your code, you source is a one-off http call which means it will not emit multiple times and the follow up action is not executing observable but to mutate an array. There is no need to use switchMap

get the arguments with which a spy was called

I am spying on method emit of EventEmitter in Angular
spyOn(answerComponent.answerEmitter, 'emit');
I want to check that emit was called with an argument A but I don't want to check exact match with A. I want to check that emit was called with values A.a, A.b and ignore value of A.c.
Is it possible to do so?
Use jasmine.objectContaining.
const emit = spyOn(answerComponent.answerEmitter, 'emit');
expect(emit).toHaveBeenCalledWith(jasmine.objectContaining({ a: <value>, b: <value>));
Two ways come to my mind:
One by using the native toHaveBeenCalledWith
expect(answerComponent.answerEmitter, 'emit').toHaveBeenCalledWith(A.a);
expect(answerComponent.answerEmitter, 'emit').toHaveBeenCalledWith(A.b);
// you can think of all the callers being tracked as an array and you can assert with
// toHaveBeenCalledWith to check the array of all of the calls and see the arguments
expect(anserComponent.anserEmitter, 'emit').not.toHaveBeenCalledWith(A.c); // maybe you're looking for this as well
You can also spy on the emit and call a fake function:
spyOn(answerComponent.answerEmitter, 'emit').and.callFake((arg: any) => {
// every time emit is called, this fake function is called
if (arg !== A.a || arg !== A.b) {
throw 'Wrong argument passed!!'; // you can refine this call fake
} // but the point is that you can have a handle on the argument passed and assert it
});

Should an rxjs operator return a function or an observable?

I'm a bit confused by the various definitions of an operator in rxjs.
Below I provide some of the definitions:
1 A Pipeable Operator is a function that takes an Observable as its input and returns another Observable.
Creation Operators are the other kind of operator, which can be called as standalone functions to create a new Observable
2 An operator is a function that takes one observable (the source) as its first argument and returns another observable (the destination, or outer observable)
3 Operators take configuration options, and they return a function that takes a source observable.
4 Operators should always return an Observable [..] If you create a method that returns something other than an Observable, it's not an operator, and that's fine.
Since 1,2,4 seem to be conflicting with 3, which definition is the correct one. Is there a better definition of rxjs operators?
For example: in case of map. Is map() itself the operator? Or the operator is the return value of map()?
Is map() itself the operator? Or the operator is the return value of map()?
Current implementation of the map() looks like this:
export function map<T, R>(project: (value: T, index: number) => R, thisArg?: any): OperatorFunction<T, R> {
return function mapOperation(source: Observable<T>): Observable<R> {
if (typeof project !== 'function') {
throw new TypeError('argument is not a function. Are you looking for `mapTo()`?');
}
return source.lift(new MapOperator(project, thisArg));
};
}
So, map() is a function. It is an operator in RxJS terms, yes, but it's still a regular JavaScript function. That's it.
This operator receives projection callback function which gets called by the map operator. This callback is something you're passing to map(), e.g. value => value.id from this example:
source$.pipe(map(value => value.id))
The return value of the map is also a function (declared as OperatorFunction<T, R>). You can tell that it is a function since map returns function mapOperation().
Now, mapOperation function receives only one parameter: source which is of type Observable<T> and returns another (transformed) Observable<R>.
To summarize, when you say:
A Pipeable Operator is a function that takes an Observable as its input and returns another Observable.
This means that an RxJS operator (which is a function) is pipeable when it takes an Observable as its input and returns another Observable which in our case is true: a map operator indeed returns a function (mapOperation) whose signature ((source: Observable<T>): Observable<R>) indicates exactly that: it takes one Observable and returns another.
An operator is a function that takes one observable (the source) as its first argument and returns another observable (the destination, or outer observable)
I already mentioned couple of times that an operator is a just function.
Operators take configuration options, and they return a function that takes a source observable.
Yes, in this case, a map() could be called operator since it receives configuration option - a projecttion callback function. So, there's really no conflicts here since many operators are configurable.
I'd say that there's conflict in this one:
Operators should always return an Observable [..] If you create a method that returns something other than an Observable, it's not an operator, and that's fine.
I guess that this is an old definition when pipeable operators weren't pipeable. Before pipeable operators were introduced (I think in version 5 of RxJS), operators were returning Observables. An old map() implementation indicates just that.
For more information about why creators of RxJS decided to introduce pipeable operators, please take a look at this document.
Another great article about what are Observables can be found here.
Also:
Creation Operators are the other kind of operator, which can be called as standalone functions to create a new Observable.
of() is an example of creation operator which returns (creates) an Observable. Please take a look at the source code.
TL;DR: A map() is a function that usually has one parameter (a projection callback function) which also returns a function that receives a source Observable and returns a destination Observable.
EDIT: To answer your question from comments, I'd like to do it here.
Yes, in RxJS 6 you can create a function that accepts observable and returns another one and that would be the operator. E.g.
function myOperatorFunction(s: Observable<any>) {
return of(typeof s);
}
and you'd call it like
source$.pipe(myOperatorFunction);
Please notice that I didn't call myOperatorFunction in pipe(), I just passed the reference to it, i.e. I didn't write myOperatorFunction with parenthesis, but without them. That is because pipe receives functions.
In cases where you need to pass some data or callback functions, like in map example, you'd have to have another function that would receive your parameters, just like map receives projection parameter, and use it however you like.
Now, you may wonder why there are operators that don't receive any data, but are still created as functions that return function, like refCount(). That is to coincide with other operators that mostly have some parameters so you don't have to remember which ones don't receive parameters or which ones have default parameters (like min()). In case of refCount, if it was written a bit different than it is now, you could write
source$.pipe(refCountOperatorFunction);
instead of
source$.pipe(refCount());
but you'd have to know that you have to write it this way, so that is the reason why functions are used to return functions (that receives observable and returns another observable).
EDIT 2: Now that you know that built in operators return functions, you could call them by passing in source observable. E.g.
map(value => value.toString())(of())
But this is ugly and not recommended way of piping operators, though it would still work. Let's see it in action:
of(1, 2, true, false, {a: 'b'})
.pipe(
map(value => value.toString()),
filter(value => value.endsWith('e'))
).subscribe(value => console.log(value));
can be also written like this:
filter((value: string) => value.endsWith('e'))(map(value => value.toString())(of(1, 2, true, false, {a: 'b'})))
.subscribe(a => console.log(a));
Although this is a completely valid RxJS code, there's no way you can think of what it does when you read the latter example. What pipe actually does here is that it reduces over all the functions that were passed in and calls them by passing the previous source Observable to the current function.
Yes. map itself is an operator.
Every operator returns an Observable so later on you can subscribe to the Observable you created.
Of course when I say 'you created' I mean that you created via a creation operator or using the Observable class: new Observable
A pipeable operator is just an operator that would be inside the pipe utility function.
Any function that returns a function with a signature of Observable can be piped and that's why you can create your own Observables and pipe operators to it.
I am pretty much sure that you already know all of this and all you want to know is why there is a conflict in what you are reading.
First, you don't have to pipe anything to your Observable.
You can create an Observable and subscribe to it and that's it.
Since you do not have to pipe anything to your Observable, what should we do with the operators?
Are they used only within a pipe?
Answer is NO
As mentioned, an operator takes configuration options, in your example:
const squareValues = map((val: number) => val * val);
and then it returns a new function that takes the source Observable so you can later on subscribe to it:
const squaredNums = squareValues(nums);
As we can see, map took (val: number) => val * val and returned a function that now gets the Observable nums: const nums = of(1, 2, 3);
Behind the scenes map will take the source observable and transform according to the configuration.
Now the important thing:
Instead of doing that in this way (which you can but no need for that), it is better to pipe your operators so you can combine all of the functions (assuming you are using more operators) into one single function.
So again, behind the scenes you can refer to pipe as a cooler way to make operations on your source Observable rather then declaring many variables that uses different operators with some configuration which return a function that takes a source Observable.

RxJS tricky zip two observables

Having getLastOrder() and getUserDetails(lastOrder.owner) async operations. (both returns Observable)
Looking for fancy operator to get combined result that will include both: [lastOrder, userDetails] who owns that order.
P.S. doing that via temporary variables does not look pretty
Not sure about fancy operators, but this can be solved quite easily by combining the operators flatMap and map. Also note that my functions just return dummy data but should work the same way with any observables:
function getLastOrder() {
return Rx.Observable.of({owner: 'foo'})
}
function getUserDetails(owner) {
return Rx.Observable.of({details: 'bar', owner})
}
function combined() {
return getLastOrder()
.flatMap(order => getUserDetails(order.owner).map(details => [order, details]))
}
When you think about prettyness, also think about the next guy who will read it.

RxJS5 Operator that can handle wrapped *and* unwrapped values?

With Promises, if you do this:
Promise.resolve(p).then(function(){
return 5;
})
.then(function(val){
return db.find(); // promise
});
we can see that then operator on promises can handle any returned value, it just calls Promise.resolve() on the return result and handles it whether it's a promise or not.
However, with RxJS5, I am finding it difficult to find an operator that can do something similar. I get a lot of errors:
TypeError: You provided x where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
at Object.subscribeToResult (/home/oleg/WebstormProjects/oresoftware/observable-persistent-queue/node_modules/rxjs/util/subscribeToResult.js:73:27)
at MergeAllSubscriber._next (/home/oleg/WebstormProjects/oresoftware/observable-persistent-queue/node_modules/rxjs/operator/mergeAll.js:85:42
for example, with this:
Rx.Observable.range(1,9)
.flatMap(function(){
return Rx.Observable.timer(4)
})
.concatAll() // this will throw the error
.subscribe();
is there an RxJS operator or pattern of operators that can handle any value and unwrap it like with Promises?
There is as far as my knowledge goes no operator which takes anything and just works. MergeMap/switchMap/concatMap will work with array/promises/observables as return values and convert them to Observable streams. But not with regular values.

Resources