Rxjs conditional switchMap based on a condition - rxjs

I have a situation like the following:
myObservable1.pipe(
switchMap((result1: MyObservable1) => {
if (condition) {
return myObservable2;
} else {
return of(null);
}
})
).subscribe(myObservable1 | myObservable2) => {
So if condition is false I only have to perform one request, if the condition is true, I have to chain the first request to the following one, they are essentially two requests to the server api.
Is there a better solution without returning that null?
Is there any conditional operator in RxJs?
Thanks!

Another possibility is to use the operator iif in a SwitchMap.
https://www.learnrxjs.io/learn-rxjs/operators/conditional/iif
https://rxjs-dev.firebaseapp.com/api/index/function/iif
but it can restrain the possibility to control specfic
conditions on your Observable :
myObservable1
.pipe(
switchMap(result1 =>
iif(() => condition
, myObservable2
, myObservable1
)
)
.subscribe(result => console.log(result));
Where you can replace 'condition' by a function returning a boolean.
With a function :
myObservable1
.pipe(
switchMap(result1 =>
iif(() => test(result1)
, myObservable2
, myObservable1
)
)
.subscribe(result => console.log(result));
test(result1) : boolean {
if(result1){
// the iif() will return myObservable2
return true;
}else{
/: the iif() will return myObservable1
return false ;
}
}
Like #amjuhire said , you can write with a filter and a switchMap :
myObservable1.pipe(
filter((result1) => condition)
switchMap((result1: MyObservable1) => {
return myObservable2;
})
).subscribe(result2 => { ... })
Another possibility given your example :
myObservable1.pipe(
switchMap((result1: MyObservable1) => {
if (condition) {
return myObservable2;
} else {
return of(result1);
}
})
subscribe(result => console.log(result));
Or using EMPTY in the switchMap :
myObservable1.pipe(
switchMap((result1: MyObservable1) => {
if (condition) {
return myObservable2;
} else {
// Observable that immediately completes
return EMPTY;
}
})
subscribe(result2 => console.log(result2));
The problem here is that we don't know the type in your observable.
So i can't judge wich way is the better.
It also depends on how you want to handle the success/error between the different calls of your observable.

No need to return of(null), you should use the RxJs filter operator
myObservable1.pipe(
filter((result1) => condition)
switchMap((result1: MyObservable1) => {
return myObservable2;
})
).subscribe((result2) => { ... })

In my understanding, you shouldn't use filter operator because in that case you wouldn't get any value.
myObservable1.pipe(
switchMap((value) => {
return iif(() => condition, myObservable2, of(value));
})
).subscribe((value) => console.log(value) // either from myObservable1 or from myObservable2)

I don't know your real code, this is the pseudo code, and I don't know which rxjs version are you using now, but you can do something like below:
assuming you are using rxjs 6:
iif(condition, obervable1, concatMap(observable1, observable2))
.subscribe()

Related

How to transform combineLatest to switchMap?

I have to observables, i know there is a way to transform combineLatest to switchMap but i dont get it
combineLatest([
faviconServiceOutgoingEvents.ready.listen().pipe(mapTo(true), startWith(false)),
this.brandService.brandName$,
]).subscribe(([isServiceReady, brandName]) => {
if (isServiceReady) {
faviconServiceIncomingEvents.changeIcon.send(brandName);
}
});
solution was near
faviconServiceOutgoingEvents.ready
.listen()
.pipe(
switchMap(() => {
return this.brandService.brandName$;
}),
)
.subscribe(brandName => faviconServiceIncomingEvents.changeIcon.send(brandName));

Required_if with more than one field in Laravel

I have the following situation: I have a form and in it I have a field that I need to validate if two other fields are true.
For example, I have the holder_name field, I need this field to be mandatory if a radio type field called paymentMethod is equal to credit_car and if another radio called card is equal to n_card
I tried to do it like this:
'paymentMethod' => 'required|credit_card',
'holder_name.*' => 'required_if:paymentMethod,credit_card',
'card' => 'required|card',
'holder_name.*' => 'required_if:card,n_card',
and so too
"holder_name" => 'required_if:paymentMethod,==,credit_card|required_if:card,==,n_card',
But I was not successful. Can anyone give me a light?? Thanks
You can use custom validation like :
"holder_name" => 'required , function($request) {
return ( $request->paymentMethod == $request->credit_card
&& $request->card == $request-> n_card) }',
Take a look at conditionally-adding-rules
You can use Custom rule for required if
holder_name.*' => Rule::requiredIf(function () {
if(somecondition){
return false;
}
return true;
}),
This method must return true or false
For ref:https://laravel.com/docs/8.x/validation#rule-required-if
Rule::requiredIf(function () {
if($this->paymentMethod==$this->credit_card && $this->card==n_card){
return true;
}
return false;
}),
If you are not using FormRequest then
Rule::requiredIf(function ()use($request) {
if($request->paymentMethod==$request->credit_card && $request->card==n_card){
return true;
}
return false;
}),
the way it worked for me was:
return Validator::make($data, [
'holder_name' => Rule::requiredIf($data['paymentMethod'] == 'credit_card'
&& $data['card'] == 'n_card')],$messages);
Tank's

Removing side effect from observable implementation

I have the following observable implementation
public getFromAddressSubjectObservable() {
let _address: string;
return this.fromAddressSubject.pipe(
tap((address: string) => _address = address),
mergeMap(() => this.rpc.call('', 'smsglocalkeys', [])),
map((keys: SmsgLocalKeysResult) => [...keys.smsg_keys, ...keys.wallet_keys]),
filter((keys: Array<SmsgLocalKey>) => !keys.some(k => k.address === _address)),
mergeMap(() => this.rpc.call('', 'smsggetpubkey', [_address])),
mergeMap((pubkeyData: PublicKeyData) => this.rpc.call('', 'smsgaddaddress', [ pubkeyData.address, pubkeyData.publickey ])),
mergeMap(() => this.rpc.call('', 'smsgaddlocaladdress', [_address]))
)
}
I would like to know if there's a way that I could have this function without side effects, i.e. passing the value of _address from the first operator to the last one of the observable.
One way could be the following. You start defining a method, closureAddressMethod, which expects address as parameter, like this
public closureAddressMethod(address: string) {
return this.rpc.call('', 'smsglocalkeys', [])).pipe(
map((keys: SmsgLocalKeysResult) => [...keys.smsg_keys, ...keys.wallet_keys]),
filter((keys: Array<SmsgLocalKey>) => !keys.some(k => k.address === _address)),
mergeMap(() => this.rpc.call('', 'smsggetpubkey', [_address])),
mergeMap((pubkeyData: PublicKeyData) => this.rpc.call('', 'smsgaddaddress', [ pubkeyData.address, pubkeyData.publickey ])),
mergeMap(() => this.rpc.call('', 'smsgaddlocaladdress', [_address]))
)
}
and then you use this method within the pipe of getFromAddressSubjectObservable method, like this
public getFromAddressSubjectObservable() {
return this.fromAddressSubject.pipe(
mergeMap(address => closureAddressMethod(address))
)
}
Last point, unrelated to your question, is about using mergeMap in a situation which I see as a chain of sequential calls to some remote server. Maybe, in such cases, you may want to consider using concatMap, as suggested in this video from Ben Lesh.

How to correctly handle errors in this effect?

I've written the Effect below to make requests for each item in the array, and when one fails, the error isn't handled and the observable stream completes resulting actions not triggering effects any further.
Effect:
#Effect() myEffect$ = this.actions$.pipe(
ofType<myAction>(myActionTypes.myAction),
mergeMapTo(this.store.select(getAnArray)),
exhaustMap((request: any[]) => {
return zip(...request.map(item => {
return this.myService.myFunction(item).pipe(
map(response => {
return this.store.dispatch(new MyActionSuccess(response))
}),
catchError(error => {
return Observable.of(new MyActionFailure(error));
})
)
}))
})
How do I handle the error in this case?

Rxjs is getting AjaxObservable instead of ajax response

I have the next Observable i trying to filter to get the user in network but .mapTo(phone => verifyPhoneInNetwork(phone, country)) return AjaxObservable instead of the ajax response
function verifyInNetwork(contacts: any, country: string) {
const inNetworkOb = Observable
.from(contacts)
.map(contact => contact.phones)
.map(phone => verifyPhoneInNetwork(phone, country))
.first(({response}) => {
return !response.invalid && !response.exists;
})
.isEmpty()
.filter(empty => empty);
If verifyPhoneInNetowrk returns an Observable you should use switchMap like so:
function verifyInNetwork(contacts: any, country: string) {
const inNetworkOb = Observable
.from(contacts)
.map(contact => contact.phones)
.switchMap(phone => verifyPhoneInNetwork(phone, country))
.first(({response}) => {
return !response.invalid && !response.exists;
})
.isEmpty()
.filter(empty => empty);
Learn more about switchMap.

Resources