How can I chain HTTP calls in Angular 2? - http

I'm new to Angular 2 and HTTP Observables. I have a component which calls an HTTP service and returns an Observable. Then I subscribe to that Observable and it works fine.
Now, I want, in that component, after calling the first HTTP service, if the call was successful, to call another HTTP service and return that Observable. So, if the first call is not successful the component returns that Observable, opposite it returns Observable of the second call.
What is the best way to chain HTTP calls? Is there an elegant way, for example like monads?

You can do this using the mergeMap operator.
Angular 4.3+ (using HttpClientModule) and RxJS 6+
import { mergeMap } from 'rxjs/operators';
this.http.get('./customer.json').pipe(
mergeMap(customer => this.http.get(customer.contractUrl))
).subscribe(res => this.contract = res);
Angular < 4.3 (using HttpModule) and RxJS < 5.5
Import the operators map and mergeMap, then you can chain two calls as follows:
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
this.http.get('./customer.json')
.map((res: Response) => res.json())
.mergeMap(customer => this.http.get(customer.contractUrl))
.map((res: Response) => res.json())
.subscribe(res => this.contract = res);
Some more details here: http://www.syntaxsuccess.com/viewarticle/angular-2.0-and-http
More information about the mergeMap operator can be found here

Using rxjs to do the job is a pretty good solution. Is it easy to read? I don't know.
An alternative way to do this and more readable (in my opinion) is to use await/async.
Example:
async getContrat(){
// Get the customer
const customer = await this.http.get('./customer.json').toPromise();
// Get the contract from the URL
const contract = await this.http.get(customer.contractUrl).toPromise();
return contract; // You can return what you want here
}
Then call it :)
this.myService.getContrat().then( (contract) => {
// do what you want
});
Or in an async function:
const contract = await this.myService.getContrat();
You can also use try/catch to manage the error:
let customer;
try {
customer = await this.http.get('./customer.json').toPromise();
}catch(err){
console.log('Something went wrong will trying to get customer');
throw err; // Propagate the error
//customer = {}; // It's a possible case
}

Related

Error in epic with redux-observable and rxjs

I'm trying to create an epic that fetches data from firestore using rxfire. On every emission of a value, an action needs to be dispatched. My codes looks like this:
const fetchMenu = (action$, _state$) => {
return action$.pipe(
ofType(Types.FETCH_MENU_REQUESTED),
flatMap((async ({ resID }) => {
const firestore = firebase.firestore();
const menuRef = firestore.collection('menus').where('resID', '==', resID);
return collectionData(menuRef, 'id')
.pipe(
map(val => {
return Creators.fetchMenuSuccess(val);
})
)
}
)),
);
};
However, I'm getting the error Actions must be plain objects. Use custom middleware for async actions.
As far as I understand, the pipe operator is wrapping my value in an observable and that's why I'm getting the error, but I'm not sure what to do so that it only returns the action. I'm still a bit new to rxjs so any help would be very appreciated.
Your flatMap callback is async, which means it's not returning an observable that emits the fetchMenuSuccess action, it's returning a promise that resolves to the observable. Rx will unwrap the promise automatically, which means that the observable returned from this epic emits observables instead of subscribing to them and emitting their own values.
Removing the async keyword should fix it.

redux asynchronous action with await/async

From the tutorial located here, I have a question regarding this section of the code:
export function fetchPosts(subreddit) {
// Thunk middleware knows how to handle functions.
// It passes the dispatch method as an argument to the function,
// thus making it able to dispatch actions itself.
return function (dispatch) {
// First dispatch: the app state is updated to inform
// that the API call is starting.
dispatch(requestPosts(subreddit))
// The function called by the thunk middleware can return a value,
// that is passed on as the return value of the dispatch method.
// In this case, we return a promise to wait for.
// This is not required by thunk middleware, but it is convenient for us.
return fetch(`https://www.reddit.com/r/${subreddit}.json`)
.then(
response => response.json(),
// Do not use catch, because that will also catch
// any errors in the dispatch and resulting render,
// causing an loop of 'Unexpected batch number' errors.
// https://github.com/facebook/react/issues/6895
error => console.log('An error occured.', error)
)
.then(json =>
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(receivePosts(subreddit, json))
)
}
}
Let's assume I wanted to use the async/await syntax instead of the "then" syntax, how would I get the error object if something fails?
e.g.
let response = await fetch(`https://www.reddit.com/r/${subreddit}.json`)
let json = await response.json();
I can surround these lines of code with a try/catch, but the author has a stern warning not to use catch here (refer to snippet above).
So is there a proper way to use the async/await pattern with this code?
In the link you provided the note to avoid using catch is regarding the promise .catch statement. This is because it would catch errors in both the then blocks. Instead of just errors caused via fetch or response.json() it would also catch errors caused via dispatch(receivePosts(subreddit, json))
You should be able to use async await as you describe in your post whilst avoiding catching errors caused by dispatch. e.g.
export function fetchPosts(subreddit) {
return async function (dispatch) {
dispatch(requestPosts(subreddit));
let response;
let json;
try {
response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
json = await response.json();
} catch(e) {
// handle fetch or json error here e.g.
dispatch(receivePostsError(subreddit, e.message));
}
if (json) {
dispatch(receivePosts(subreddit, json));
}
}
}

Redux - Jest: Testing functions that have void return

New to Jest and Redux and I'm having trouble with testing functions that are dispatching to the store but don't yield a return value. I'm trying to follow the example from the Redux website does this
return store.dispatch(actions.fetchTodos()).then(() => {
// return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
however I have several "fetchtodos" functions that don't return anything which causes the error TypeError:
Cannot read property 'then' of undefined due to returning undefined
I'm wondering what I can do to test that my mock store is correctly updating. Is there a way to dispatch the function, wait for it to finish and then compare the mock store with expected results?
Thanks
Edit: We're using typescript
action from tsx
export function selectTopic(topic: Topic | undefined): (dispatch: Redux.Dispatch<TopicState>) => void {
return (dispatch: Redux.Dispatch<TopicState>): void => {
dispatch({
type: SELECT_Topic,
payload: topic,
});
dispatch(reset(topic));
};
}
test.tsx
const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);
describe('Select Topic action', () => {
it('should create an action to select .', () => {
const topic: Topic = mockdata.example[0];
const expectedAction = {
type: actions.SELECT_TOPIC,
payload: topic,
};
const store = mockStore(mockdata.defaultState);
return store.dispatch(actions.selectTopic(topic)).then(() => {
expect(store.getState()).toEqual(expectedAction);
});
});
});
The action is what I'm given to test(and there are many other functions similar to it. I'm getting that undefined error when running the test code, as the function isn't returning anything.
In Redux, the store's dispatch method is synchronous unless you attach middleware that changes that behavior, ie: returns a promise.
So this is likely a redux configuration problem. Be sure you are setting up your test store with the same middleware that allows you to use the promise pattern in production.
And as always, be sure to mock any network requests to avoid making api calls in test.

Configure react redux middleware from fetch

I have a Redux middleware that requires some data to be configured via a server call (eg, fetch) which is async / requires promises.
However, every example of createStoresuch as this one seems to use a Singleton pattern for the store. This means that the store gets initialized before my fetch is complete. Because it's middleware, I can't "reconfigure" it after createStore is called.
How can i configure createStore without using a singleton pattern to configure middleware?
How do you fetch those data? If it's just a simple API call. You can easily wait for the data to be returned then pass the data to createStore. Something like this:
const fetchData = () => {
return Promise.resolve({
data: 'Your data here'
});
}
const initialState = window.__INITIAL_STATE__;
fetchData()
.then((data) => {
initialState.somethingYouNeed = data;
const store = createStore(initialState);
// Do the rest here
});

How to set a timeout in Angular 2 when using HTTP + toPromise()?

I found this answer where the solution proposed is to use Observables to set http requests' timeout.
However my code is structured to use mostly promises (I use observables where I want to have data automatically updated - that's not the case of the API calls).
Here is my code (inspired by Angular 2 tutorial):
makePostRequest(requestUrl:string, requestBody: any, requestOptions?: RequestOptions): Promise<any> {
requestOptions = requestOptions || new RequestOptions({ headers: this._defaultHeaders });
return this._http.post(requestUrl, JSON.stringify(requestBody), requestOptions)
.toPromise()
.then(this.extractData)
.catch(this.handleError)
}
How to set a timeout and throw an error (if the timeout expires) that I then catch in .catch() or - alternatively - replicate the exact precise behavior with Observables (including converting the result to a Promise and not monitoring for monitoring for API update(*))?
(*) NOTE: I'm not sure whether Observables keep calling the APIs to check for new data, but that's not the point of my question, I just want to make sure this behavior does not occur.
I would expect this to do what you want (not tried):
makePostRequest(requestUrl:string, requestBody: any, requestOptions?: RequestOptions): Promise<any> {
requestOptions = requestOptions || new RequestOptions({ headers: this._defaultHeaders });
return this._http.post(requestUrl, JSON.stringify(requestBody), requestOptions)
.timeout(3000, new Error('timeout exceeded'))
.toPromise()
.then(this.extractData)
.catch(this.handleError)
}
From Angular2 timeout in http post request
The solution (right chain + imports) I found:
// ! must import these
...
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
return this._http.get(requestUrl, requestOptions)
.timeout(5000, new Error( 'HTTP (GET) timeout for path: ' + requestUrl))
.map(this.extractData)
.toPromise()
.catch(this.handleError);
I approached this a bit differently. I had logic that relied on a promise being returned—and doing .timeout caused it to immediately fail regardless of the timeout duration.
My solution was to create a new promise instead of using toPromise:
const timeoutInMs = 3000;
const request = this._http
.post(/* ... */)
.timeout(timeoutInMs);
return new Promise((resolve, reject) => {
request
.take(1)
.subscribe(
data => resolve(data),
error => reject(error),
);
});
If you use this a lot, you could refactor it into a function (not yet tested):
const toPromiseWithTimeout = <T>(obs: Observable<T>, ms): Promise<T> =>
new Promise<T>((resolve, reject) => {
obs
.timeout(ms)
.take(1)
.subscribe(
data => resolve(data),
error => reject(error),
);
});
And to use it:
const timeoutInMs = 3000;
const request = this._http
.post<ResponseType>(/* ... */);
return toPromiseWithTimeout(request, timeoutInMs);

Resources