Rxjs conditional delay - rxjs5

I want an observable to be delayed depending on its value. For example:
of(someBool).pipe(delay(1000))
skip delay when someBool is false, but wait a sec when it's true.

You can use delayWhen for that:
of(someBool).pipe(
delayWhen(val => val ? interval(1000) : of(undefined))
)
Side note, according to the docs using empty() instead of of() should IMHO work, but doesn't appear to. I believe this might be a bug. I have reported it.

Example. Suppose you are implementing a login page and you want to wait until you get some token to put it in another Observable or Cookies. Then you can wait a value of your Observable (someBool here I used this.authService.isLoggedIn). So you can do something like this:
return this.authService.isLoggedIn
.pipe(
delayWhen(loggedIn => loggedIn ? interval(0) : interval(10000)),
);
And when a state of isLoggedIn is changed a user will be logged in.

Note: In future versions, empty notifiers will no longer re-emit the source value on the output observable.
someBool$.pipe(
delayWhen(val => val ? timer(1000) : timer(0))
)

Related

Can I update the deps array in useEffect?

I am using Effect Hook and want to confirm if this implementation is correct. I am not getting any warning in the console but I want to know why this is not going into an infinite loop?
React.useEffect(() => {
setSelections(inputUPC, false);
//console.log(props.uncheckCard);
props.setUncheckCard(false);
}, [props.uncheckCard]);
It's not going into infinite loop because you only set this to false so there is no change in parameter (useEffect only trigger on dependencies values change)

Cypress : The submit button is disabled even after all the text fields have filled

I was trying to automate a text submit form using cypress. The 'Create student' button is disabled even after all the fields have been filled
Please see the cypress error
code :
it('should be able to add a new student and update the details, remove from the class and delete the account', function () {
cy.visit(
'https://readingeggs.blake-staging.com/district_ui#/reading/manage-schools/students/195286/new'
)
cy.findByLabelText('First Name').type('ark')
cy.get('#first-name').should('have.value', 'ark')
cy.findByLabelText('Last Name').type('last')
cy.get('#last-name').should('have.value', 'last')
cy.get('[data-test-select-grade]').select('1')
cy.get('#grade-dropdown').should('have.value', '1')
cy.get('[data-test-select-teacher]').select('Lehner, Abbey')
cy.get('#teacher-dropdown').should('have.value', '3068134')
cy.get('[data-test-submit-new-student]').click()
cy.get('#main')
.findByRole('alert')
.should('include.text', `Successfully created a student`)
})
})
Be careful using click({force:true}) as suggested in the error message, there may be another problem that your test will now ignore!
You can first try an assertion that the button is not disabled.
Sometimes the test can run too quickly, and the web page has not yet enabled the button before the test tries to click it.
Adding .should('not.be.disabled') will retry this check for up to 4 seconds, which should be enough time for the page to complete changes.
cy.get('[data-test-submit-new-student]')
.should('not.be.disabled')
.click()
If using .should('not.be.disabled') does not work (I agree, it should be the first thing to try), try adding a trigger event to each input - in case the .type() command is not triggering the validation change.
cy.findByLabelText('First Name').type('ark').trigger('change')
cy.get('#first-name').should('have.value', 'ark')
cy.findByLabelText('Last Name').type('last').trigger('change')
cy.get('#last-name').should('have.value', 'last')
cy.get('[data-test-select-grade]').select('1').trigger('change')
cy.get('#grade-dropdown').should('have.value', '1')
cy.get('[data-test-select-teacher]').select('Lehner, Abbey').trigger('change')
cy.get('#teacher-dropdown').should('have.value', '3068134')
cy.get('[data-test-submit-new-student]').click()
If still no joy, use .click({force:true})
By the way, cy.get('[data-test-select-grade]').select('1') looks a bit suspicious. The select command can take a display value as a string or a position value as a number. The screenshot shows "K" is selected, so I would expect either of these to work
cy.get('[data-test-select-grade]').select(1) // number passed
// or
cy.get('[data-test-select-grade]').select('K') // string passed
One option would be to use {force: true} with click().
it('should be able to add a new student and update the details, remove from the class and delete the account', function () {
cy.visit(
'https://readingeggs.blake-staging.com/district_ui#/reading/manage-schools/students/195286/new'
)
cy.findByLabelText('First Name').type('ark')
cy.get('#first-name').should('have.value', 'ark')
cy.findByLabelText('Last Name').type('last')
cy.get('#last-name').should('have.value', 'last')
cy.get('[data-test-select-grade]').select('1')
cy.get('#grade-dropdown').should('have.value', '1')
cy.get('[data-test-select-teacher]').select('Lehner, Abbey')
cy.get('#teacher-dropdown').should('have.value', '3068134')
cy.get('[data-test-submit-new-student]').click({force: true})
cy.get('#main')
.findByRole('alert')
.should('include.text', 'Successfully created a student')
})

RXJS sequential HTTP request

I would like to do sequential HTTP calls and merge the responses into one and unique observable.
Problem is: the way I am doing it seems to be wrong. Indeed, I would like to merge the data from the second request into the first one, but the way I did it seems to replace the result of the first one by the result of the seconde one.
Here it is the first request called:
[{vehicule_id:123, statistics:{speed:60,rotation:38}},...]
The second one:
[{vehicule_id:123, name:"name",description:"description},...]
And the result I would like to get:
[{vehicule_id:123, statistics:{speed:60,rotation:38}, name:"name",description:"description},...]
Important thing to know: the second request needs a vehicule_id provided in the response of the first one.
With my code, the response of the second call replace the result of the first one instead of merging them.
Here it is my code:
getUserVehiculesInfo(user_id: number, language: string): void {
this._data.getUserVehiculesInfo(user_id, language)
.pipe(
map(res => res.data[user_id]),
switchMap(vehicules => forkJoin(vehicules.map(vehicule => this._data.getVehiculesInfo(tank.vehicule_id, language)))),
tap(console.log)
)
.subscribe();
}
Once you have the vehiculesInfo (the result of the forkJoin, you simply need to combine them with the vehicules:
switchMap(
vehicules => forkJoin(...).pipe(
map(infos => combineVehiculesAndInfos(vehicules, infos))
)
)

If an event is fired during throttleTime's timeout, replay last event after timeout completes and begin timeout again

What I'd like to see is this output:
|---0--0--0-----0---------------> Events
|---0-------0-------0-----------> Output
I supply a duration, the stream allows the first event through, waits the duration amount. If an event was triggered during the timeout period, emit and start the timeout again.
The values don't matter in my case, only knowing events happened.
How would I achieve this with RXJS? I started with throttleTime, but throttleTime doesn't emit the trailing event when its timeout finishes.
subject
.pipe(
throttleTime(5000)
)
.subscribe(
event => action()
)
Check out the docs for throttleTime. You can pass in an optional config and specify leading / trailing behavior. What you ran into was this:
config:
Optional. Default is defaultThrottleConfig.
a configuration object to define leading and trailing behavior. Defaults to {
leading: true, trailing: false }.

RxJs. How to wait for other observable if it fired recently

Basically I have two subscriptions that fire events now and then.
First one is pretty straightforward, it loads and displays some data:
observableOne$.switchMap(event => {
return loadData(event);
}).subscribe(data => {
displayData(data);
});
Second one transform displayed data, and the transformation should be done only once:
observableTwo$.subscribe(event => {
transformDisplayedData(event);
});
The problem I am facing is that if second event is fired right after first, then transformation is lost. And I can't reapply, it is one time action.
Is there a way to postpone transformation if observableOne$ fired recently?
Time sequence will look like so:
One : -a----------a----->
Two : -----b------b----->
Display : ---a----------a---> // lag is due to async data loading
Transform: -----b--------b--->
EDIT
I have created a fiddle with demonstration. The actual time sequence can be written like this:
PageClick : -x----------x----->
HighlightClick : -----x------x----->
DataRender : ---x----------x--->
DataHighlight : -----x------x----->
When PageClick and HighlightClick come together data is highlighted and a second later rendered. But I want to postpone highlighting, hence desired time sequence:
PageClick : -x----------x----->
HighlightClick : -----x------x----->
DataRender : ---x----------x--->
DataHighlight : -----x--------x--->
If someone interested it can be done via introducing intermediate stream, through which events start loading and end loading are passing. For more details refer to this question

Resources