I have an angular project and I have some api that creates a line item and then loops through and calls the api to add each modification to the line item and finally it fires the print request. Right now I have to do this using multiple subscribes and was thinking if there was away to modify the service itself so it runs the api's in order and returns the response so it shortens the code as well. The loops make it a bit complicated for exhaustmap or mergemap.
The function in my component that calls the api's in order are:
addLineItems() {
this.lineItems = this.lineItems.filter(function (item) {
delete item.image;
return true;
});
console.log('In Line items' this.orderId);
this.storedMerchantId = this.currentUser.merchantId
this.storedCloverToken = this.currentUser.cloverToken;
console.log('line Item Is: ' JSON.stringify(this.lineItems));
this.lineItems.forEach(lineItem => {
console.log(JSON.stringify(lineItem));
this.cloverOrderService
.postNewLineItem(
this.orderId,
lineItem,
this.storedMerchantId
)
.subscribe(cloverItem => {
lineItem.id = cloverItem[0].id;
console.log(JSON.stringify(lineItem));
lineItem.newModifications.forEach(modification => {
this.cloverOrderService
.postNewModification(
this.orderId,
lineItem.id,
modification,
this.storedMerchantId
)
.pipe(first())
.subscribe(itemModification => {
console.log(
'modification added:'
JSON.stringify(itemModification)
);
},
error => {
this.alertService.error(error);
this.loading = false;
});
});
console.log(JSON.stringify(lineItem));
this.cartService.SetOrderHistoryToCart();
this.cartService.removeFromCart(lineItem);
});
});
this.orderPlaced();
}
The code for the service functions for the api calls are:
postNewLineItem(id: string, lineItem: LineItem, merchantId: string) {
this.cloverMerchantIdAndTokenSet();
const body = {
'merchant_id': this.merchant_id,
'token': this.cloverToken,
'order_id': id,
'line_item': lineItem
};
const newLineItem = this.http.post<CloverOrder>(`${environment.apiUrl}/cloverOrders/createLineItem`, body);
const printOrder = this.fireOrder(id, merchantId);
//return forkJoin(newLineItem, printOrder);
return this.http.post<any>(`${environment.apiUrl}/cloverOrders/createLineItem`, body);
}
fireOrder(id: string, merchantId: string) {
this.cloverMerchantIdAndTokenSet();
const body = {
'merchant_id': this.merchant_id,
'token': this.cloverToken,
'printItem': new PrinterObject('PrintOrder', id),
'order_id': id
};
return this.http.post<any>(`${environment.apiUrl}/cloverOrders/firePrint`, body);
}
postNewModification(id: string, lineItemID: string, modification: Modification, merchantID: string) {
this.cloverMerchantIdAndTokenSet();
const body = {
// 'merchant_id': localStorage.getItem('merchant_id'),
'merchant_id': this.merchant_id,
'token': this.cloverToken,
'order_id': id,
'lineitem_id': lineItemID,
'modification': modification
};
return this.http.post<any>(`${environment.apiUrl}/cloverOrders/createItemModification`, body);
}
CodePudding user response:
You need to use Higher Order Operators from RxJs
to transform multiple streams and subscribe only once. You can choose an operator such as mergeMap
, switchMap
or concatMap
among others, it depends on what you want to do with your different streams and the relation between them. As stated in the learnRxJs official website:
For instance, when using switchMap each inner subscription is completed when the source emits, allowing only one active inner subscription. In contrast, mergeMap allows for multiple inner subscriptions to be active at a time. Because of this, one of the most common use-case for mergeMap is requests that should not be canceled, think writes rather than reads. Note that if order must be maintained concatMap is a better option.
Read here for how to use them with exmaples and what is best for you.
CodePudding user response:
first of all you need to know that from
enables you to transform an array into a stream, then the stream contains an element of the array, the next problem is abuse of subscribe
. You should pipe
the value and transform by rxjs operators
I've created an example, how you can use a from
instead of a loop
.
function fromInsteadLoop() {
const items = [1, 2, 3];
from(items)
.pipe(map((item) => item 1))
.subscribe((item) => {
console.log(item);
// console.log(item) -> 2
// console.log(item) -> 3
// console.log(item) -> 4
});
}
Also I've created solution but I'm not sure it's good:
from(this.lineItems)
.pipe(
mergeMap((lineItem) => {
return zip(
this.cloverOrderService.postNewLineItem(
this.orderId,
lineItem,
this.storedMerchantId
),
of(lineItem)
);
}),
mergeMap(([_, lineItem]) => {
return from(
lineItem.newModifications.map((modification) => ({
modification,
lineItem,
}))
);
}),
mergeMap(({ modification, lineItem }) => {
return this.cloverOrderService.postNewModification(
this.orderId,
lineItem.id,
modification,
this.storedMerchantId
);
})
)
.subscribe({
next: (itemModification) => console.log(itemModification),
error: (error) => {
this.alertService.error(error);
this.loading = false;
},
});