How can i rewrite the following code with switchmap - rxjs

Hi I have a small code snippet using rxjs library as follows. It is working fine . However I would like to re write it using switchmap . I tried all option however i am getting an error . was wondering if someone could help me out .
this.campaignControl.valueChanges.subscribe(
(value) => {
this.flightsService.getUnpaginatedFlightsWithinRange(
{
campaignId : value,
jobStatuses: this.flightStatusIds,
rangeStartDate:
(this.rangeControl.value[0]).toISOString(),
rangeEndDate: (this.rangeControl.value[1]).toISOString()
}
).subscribe(
(flightsUnpaginated) => {
this.flights = flightsUnpaginated;
}
);
}
);
thank you

I believe you're looking for something like this:
this.campaignControl.valueChanges.pipe(
switchMap(value => this.flightsService.getUnpaginatedFlightsWithinRange({
campaignId : value,
jobStatuses: this.flightStatusIds,
rangeStartDate: (this.rangeControl.value[0]).toISOString(),
rangeEndDate: (this.rangeControl.value[1]).toISOString()
}),
tap(flightsUnpaginated => this.flights = flightsUnpaginated)
).subscribe();

Related

Cypress: The 'task' event has not been registered in the plugins file. You must register it before using cy.task()

I am writing the end-to-end tests using Cypress for my web application. In my tests, I am trying to create a task, https://docs.cypress.io/api/commands/task. But it is throwing an error. Here is what I did.
I declared a task in the plugins/index.js file as follow.
module.exports = (on) => {
on("task", {
setTestId(id) {
testId = id;
return null;
},
getTestId() {
return testId;
}
});
};
Then I use the task in the test as follow.
cy.task('setTestId', 7654321);
When I run the tests, I am getting the following error.
The 'task' event has not been registered in the plugins file. You must register it before using cy.task()
As you can see, I tried this solution as well, Cypress task fails and complains that task event has not been registered in the plugins file. It did not work either. What is wrong with my code and how can I fix it?
You're missing some formatting (":" and "=>" and such). Try this:
module.exports = (on) => {
on("task", {
setTestId: (id) => {
testId = id;
return null;
},
getTestId: () => {
return testId;
}
});
};
Set the testId as you did:
cy.task('setTestId', 7654321);
And when you want to retrieve the testId, use:
cy.task('getTestId').then((testId) => {
cy.log(testId)
});

Cypress - extract URL info

I have this URL :
https://www.acme.com/book/passengers?id=h1c7cafc-5457-4564-af9d-2599c6a37dde&hash=7EPbMqFFQu8T5R3AQr1GCw&gtmsearchtype=City+Break
and want to store these values :
id=h1c7cafc-5457-4564-af9d-2599c6a37dde
hash=7EPbMqFFQu8T5R3AQr1GCw
for use in a later test.
How do I extract these values from the URL? I am using Cypress. Thanks.
Please follow the following steps and that's all there is to it.
You can put this snippet into before() hooks of your spec file and you can access them wherever you want.
cy.location().then(fullUrl => {
let pathName = fullUrl.pathname
let arr = pathName.split('?');
let arrayValues = arr[1].split('&');
cy.log(arrayValues[0]);
cy.log(arrayValues[1]);
cy.log(arrayValues[2]);
})
In case anyone needs the correct answer, use the cy.location('search') to extract the search part of the location data.
Then for convenience, convert it to a javascript object with key/value pairs for each item.
Finally, store it in a Cypress alias to use later in the test.
cy.location('search')
.then(search=> {
const searchValues = search.split('?')[1].split('&')
// yields: [
// id=h1c7cafc-5457-4564-af9d-2599c6a37dde,
// hash=7EPbMqFFQu8T5R3AQr1GCw,
// gtmsearchtype=City+Break
// ]
const searchMap = searchValues.reduce((acc,item) => {
const [key,value] = item.split('=')
acc[key] = value.replace('+', ' ')
return acc
}, {})
// yields: {
// id: "h1c7cafc-5457-4564-af9d-2599c6a37dde",
// hash: "7EPbMqFFQu8T5R3AQr1GCw",
// gtmsearchtype: "City Break"
// }
cy.wrap(searchMap).as('searchMap')
})
Using #Srinu Kodi's answer I got it working changing ...then(fullUrl => ... to
...then((fullUrl) => ...

How to run some code in an RxJS chain given there were no errors

I am trying to find a way to run some code only if there was no error in a given rxjs chain. Consider the following, is there something like the artificial NO_ERROR_OCCURED_RUN_HAPPY_PATH_CODE operator in rxjs?
private wrap(obs: Observable<any>): Observable<any> {
return of(1).pipe(
tap(() => this.spinner.startSpinner()),
mergeMap(() =>
obs.pipe(
NO_ERROR_OCCURED_RUN_HAPPY_PATH_CODE(() => this.generic_success_popup()),
catchError(this.handleError),
)
),
finalize(() => this.spinner.stopSpinner())
);
}
Basically almost all operator will be invoke if no error is thrown along the pipe, apart from finalize
obs.pipe(
tap(_=>console.log('no error, will run'),
// throw some error
map(_=>throwError('some error'),
finalize(_=>console.log('will be called when there is error or upon observable complete')),
tap(_=>console.log('this will not run')),
catchError(this.handleError),
)

Says Jasmine spy is not being called, but I can see that its being called

I can't figure out why Jasmine is claiming that the function I'm spying on isn't being called, especially since it is logging in buildLinksObj when called through and not calling when I remove .and.callThrough() I feel like I've written similar code a bunch of times before without any problem. I'm using Jasmine 2.9
The error message I'm getting is:
1) addToLinks should call buildLinksObj if its given an object with children
it should add the personalized links to PageApp.meta.analytics.links
Expected spy buildLinksObj to have been called.
at UserContext.<anonymous> (http://localhost:9877webpack:///tests/specs/common/FetchPersonalContent.spec.js:854:0 <- tests/app-mcom.js:104553:48)
Here's the except of my code:
FetchPersonalContent.js
const buildLinksObj = (responseObj = {}, targetObj, PageApp) => {
console.log('it logs in buildLinksObj') // This is logging!
}
const addToLinks = (responseArr, personalizedLinks) => {
responseArr.forEach((media) => {
const type = media.type;
const typeObj = media[type];
buildLinksObj(typeObj, personalizedLinks, PageApp);
if (typeObj && typeObj.children) {
console.log('has children!')
console.log('typeObj.children is: ', typeObj.children);
typeObj.children.forEach((child) => {
console.log('has a child')
buildLinksObj(child, personalizedLinks, PageApp);
console.log('buildLinksObj was definitely called. what the heck?')
});
}
});
}
export {buildLinksObj, addToLinks, FetchPersonalContent as default,
};
FetchPersonalContent.spec.js
import * as FetchPersonalContent from '../../../src/FetchPersonalContent'; // my path is definitely correct
describe('it should add the personalized links to PageApp.meta.analytics.links', () => {
it('addToLinks should call buildLinksObj if its given an object with children ', () => {
spyOn(FetchPersonalContent, 'buildLinksObj').and.callThrough();
FetchPersonalContent.addToLinks([{
"personalId": 30718,
"type": "carousel",
"carousel": {}
}], {});
expect(FetchPersonalContent.buildLinksObj).toHaveBeenCalled();
});
});
I'd really appreciate any help!
I have a feeling FetchPersonalContent.buildLinksObj in the spec file is not pointing to the same instance as buildLinksObj in the FetchPersonalContent.js file.
Why is export {FetchPersonalContent as default} required? I am assuming you have shared the complete content of FetchPersonalContent.js in your question.
Possible solutions:
You can try removing FetchPersonalContent from the export statement.
Or
Instead of
export {buildLinksObj, addToLinks, FetchPersonalContent as default,
};
You can directly export the constants in FetchPersonalContent.js file.

combineLatest deprecated in favor of static combineLatest

After running the rxjs migration tool using
rxjs-5-to-6-migrate -p src/tsconfig.app.json
I'm now getting a linting error:
combineLatest is deprecated: Deprecated in favor of static
combineLatest.
Here is my code before running the migration command:
this.store.combineLatest(
this.store.select(lang.getCurrent),
this.store.select(lang.getCurrentLocale)
).subscribe(([state, currentLang, locale]) => {
this._language = session.language === currentLang ? '' : currentLang;
this._locale = session.locale === locale ? '' : locale;
});
My code after running the migration command: (currently presenting a linting error)
import {map, combineLatest} from 'rxjs/operators';
this.store.combineLatest(
this.store.select(lang.getCurrent),
this.store.select(lang.getCurrentLocale)
).subscribe(([state, currentLang, locale]) => {
this._language = session.language === currentLang ? '' : currentLang;
this._locale = session.locale === locale ? '' : locale;
});
The question was asked in this stackoverflow questions, but it was not specific enough: Angular 6 ng lint duplicate errors and warnings, combineLatest is deprecated
.
In rxjs 6.5+
import { combineLatest } from 'rxjs';
combineLatest([a$, b$, c$]);
And for most applications it's helpful to map the array of observables to a new value as well:
combineLatest([a$, b$, c$]).pipe(
map(([a$, b$, c$]) => ({
a: a$,
b: b$,
c: c$
}))
);
Also see: https://www.learnrxjs.io/learn-rxjs/operators/combination/combinelatest
Deprecated!
Please refer to ofir fridman's answer for the correct syntaxs as of RxJs 6.5
I found an answer in this article titled: RxJS 6: What's new and what has changed? ( which comes from official docs):
The solution is to convert:
import { combineLatest } from 'rxjs/operators';
a$.pipe(combineLatest(b$, c$));
into:
import { combineLatest } from 'rxjs';
combineLatest([a$, b$, c$]);
rxjs version 6.4.0
You should import map operator from RxJs operators for it to work
combineLatest(a$, b$, c$).pipe(map([a, b, c]) => treat(a, b, c))
Seems combineLatest has been deprecated and passing an array as suggested in the various answers still throws the error in rxjs v.7.0.0 Deprecation Notice
Replaced with combineLatestWith. Will be removed in v8.
For those using rxjs v.7.0.0 and above you will need to use combineLatestwith
input1Changes$.pipe(
combineLatestWith(input2Changes$)
)
import { combineLatest } from 'rxjs';
combineLatest([
this.store.select(lang.getCurrent),
this.store.select(lang.getCurrentLocale)
]).subscribe(([state, currentLang, locale]) => {
this._language = session.language === currentLang ? '' : currentLang;
this._locale = session.locale === locale ? '' : locale;
});
combineLatest with a list of subscribers:
combineLatest([aSubscriber, bSubscriber]).pipe(map((aRes, bRes)=>{
// your business logic
}));

Resources