I have two subjects: networkStateSubject and authStateSubject.
networkStateSubject emits boolean value:
true if network connectivity is enabled,
false if network connectivity is disabled.
authStateSubject emits boolean value:
true if user logged in,
false if user logged out.
I need to combine these two subjects, so that whenever both of their values are true, new value (void) is emitted from combined observable.
You can use combineLatest and filter to check for both values.
const connectedAndAuth$ = combineLatest([networkStateSubject, authStateSubject])
.pipe(
filter(([isConnected, isAuth]) => isConnected && isAuth),
map(_ => 1) //what do you mean emit a void value?
);
I would use combineLatest with a selector networkStateSubject && authStateSubject, and then filter the true ones
Related
Given an ngrx selector:
store.select('count')
I want to create an observable that will emit values emitted by the selector, then emit another specific value after a delay.
Using concat doesn't work as (I assume) the selector doesn't complete, so the 0 is never emitted:
this.count$ = concat(store.select('count'), of(0).pipe(delay(2000)));
Stackblitz: https://stackblitz.com/edit/so-selector-delay?file=src/app/my-counter/my-counter.component.ts
- click 'Increment' button - Current Count should change to 1 then back to 0 after 2 seconds.
If you want to emit the store.select('count') value, then essentially reset it to 0 after not receiving an emission for 2 seconds, you can use a switchMap to create a source that emits two values:
The emitted count
The "default" value of 0 after 2000ms
The trick here is that the second value (0) will NOT be emitted if another store.select('count') value is received, because switchMap will create a new source and "switch" to that:
this.count$ = store.select('count').pipe(
switchMap(count => concat(
of(count),
of(0).pipe(delay(2000))
))
);
Here's a working StackBlitz demo.
It might even be worth creating a custom operator:
this.count$ = this.store.select('count').pipe(
resetAfterDelay(0, 2000)
);
export function resetAfterDelay<T>(defaultValue: T, delayMs: number) {
return switchMap((value: T) => concat(
of(value),
of(defaultValue).pipe(delay(delayMs))
));
}
StackBlitz
Below is an approach using combineLatest and BehaviorSubject
We are hold a value in a subject and create a timer that emits value 0 after 2s. So we have two Observables, one emits immediately and the other after 2s. We combine this two and the effect is a single observable as desired
valueHolderSubject$ = new BehaviorSubject(0);
...
this.count$ = combineLatest([
store.select("count").pipe(
tap(x => this.valueHolderSubject$.next(x)),
tap(() =>
timer(2000).subscribe({
next: () => {
this.valueHolderSubject$.next(0);
}
})
),
map(() => this.valueHolderSubject$.value),
distinctUntilChanged()
),
this.valueHolderSubject$
]).pipe(map(([, x]) => x));
Demo Here
A per my comments on the answer from BizzyBob, I was getting unreliable results. But I refined it to work using:
this.count$ = merge(
store.select('count'),
store.select('count').pipe(delay(2000), map(_ => 0))
);
See stackblitz: https://stackblitz.com/edit/so-selector-delay-merge-working?file=src/app/my-counter/my-counter.component.ts
I need the help in the following vue-rx / RxJs question.
I have to make a subscription in vue-rx that watches a props value, and when it is true then it calls a http request in every 500 ms, and stops it when it is false, or when the returned value is 'COMPLETED'.
I tried something like this:
export default {
props: ['started'],
subscriptions() {
return {
currentHttpState: this.$watchAsObservable('started')
.pipe(pluck('newValue'),filter(value => value === false))
.switchMap(() => interval(500).pipe(switchMap(() => this.callHttpRequest()),distinctUntilChanged())),
Thank you for the help!
I'm not too familiar with vue (or vue-rx), so this may just be half the answer (the RxJS bit).
I'm assuming this.$watchAsObservable('started') with pluck('newValue') is a stream of true and false? (reflecting the value of the started prop)
If so, I would use switchMap to switch between an interval/timer and nothing.
currentHttpState: this.$watchAsObservable('started').pipe(
pluck('newValue'),
map(val => val? timer(0,500) : EMPTY),
switchMap(timer$ => timer$.pipe(
switchMap(_ => this.callHttpRequest()),
takeWhile(result => result.status !== 'COMPLETED')
)
)
that second switchMap will have the effect that if a call takes over 500ms to complete, it will be dropped and you'll never see the results. This also unsubscribed if the takeWhile() condition isn't met - so you'll have to change that to meet your specific requirements.
I have a BehaviorSubject which emits JavaScript objects periodically. I want to construct another observable which will emit both previous and current values of the underlying observable in order to compare two objects and determine the delta.
The pairwise() or bufferCount(2, 1) operators are looking like a good fit, but they start emitting only after buffer is filled, but I require this observable to start emitting from the first event of the underlying observable.
subject.someBufferingOperator()
.subscribe([previousValue, currentValue] => {
/** Do something */
})
;
On first emission the previousValue could be just null.
Is there some built-in operators that I can use to achieve the desired result?
Actually, it was as easy as pairing pairwise() with startWith() operators:
subject
.startWith(null) // emitting first empty value to fill-in the buffer
.pairwise()
.subscribe([previousValue, currentValue] => {
if (null === previousValue) {
console.log('Probably first emission...');
}
})
;
Here's a simple operator:
function withPreviousItem<T>(): OperatorFunction<
T,
{
previous?: T;
current: T;
}
> {
return pipe(
startWith(undefined),
pairwise(),
map(([previous, current]) => ({
previous,
current: current!
}))
);
}
The nice thing about this is that the result has meaningful property names and correct types:
previous is T | undefined
current is T (not T | null)
Stackblitz example
Here's the snippet for rxjs 6+
subject
.pipe(
startWith(undefined),
pairwise()
)
.subscribe(([previousValue, currentValue]) => {
/** Do something */
});
The value in startWith() should be undefined because there is no value. Typically null is defined as "we have a value and this value is empty".
scan (RX equivalent of a reduce) is an option here:
subject
.scan((accumulator, currentValue) => {
const previousValue = ...accumulator.slice(-1);
return [previousValue, currentValue];
}, [null]) // emitting first empty value to fill-in the buffer
.subscribe(([previousValue, currentValue]) => {
// ...
});
This can be extended to a more general case when you want to look at more than two items.
I'm trying to subscribe to a "loading" Observable which returns a boolean. The goal is to have a delay for the items in the stream if loading is true, but not if loading is false. It should also be possible to ignore an outdated item, lets assume following case:
In the stream are two items:
Item A with value true, and is passed immediately
Item B with value false, and is passed 200ms later
Item A has value true, because of that it will be delayed by 500ms, Item B will not be delayed. In this situation Item B will arrive before Item A. I'm searching for an solution to completely ignore Item A for the mentioned case.
Here you see my current not-working solution:
this.loading$
.pipe(
distinctUntilChanged(),
delayWhen(loading => timer(loading ? 500 : 0))
)
.subscribe(loading => {
// Do something
});
Just replace delayWhen with switchMap and it should work. switchMap can cancel outdated inner stream. And you probably an one-time observable rather than an timer:
this.loading$
.pipe(
distinctUntilChanged(),
switchMap(
loading =>
of(loading).pipe(delay(loading ? 500 : 0)))
)
.subscribe(loading => {
// Do something
});
I have a stream which needs to skip while bool is true.
However, when that bool is set back to false, I need to apply the last stream value missed when it was skipping.
Rx.Observable
.interval(1000)
.skipWhile(val => true|false)<---when back to false, get last missed value?
.subscribe(val => console.log(val));
Use multicast to share start and the rest values. pairwise to pairt prev and next value and concat to combine start and the rest.
Rx.Observable.interval(500)
.multicast(new Rx.Subject(), shared => {
let start$ = shared
.pairwise()
.skipWhile((x)=>x[1]<5) // this is the condition to skip
.first()
.switchMap(el=>Rx.Observable.concat(Rx.Observable.of(el[0]),
Rx.Observable.of(el[1])));
return Rx.Observable.concat(start$, shared);
}).subscribe(x=>console.log(x));