I have a form with multiple posts.
User has the option to comment on one post and submit or comment on multiple posts and submit.
The problem which I’m facing is that I have just one API to save user comment.
for eg:- http://localhost:4200/post/postid
To let user know the status in case of saving all comments, is there a way to show loading with %age for each successful / failed response.
I’m currently looking to solve using Rxjs operators but other solution is also appreciated.
Thanks in advance 🙂
This is an example of a post
{
post: {
"userId": 1,
"postId": 1,
"title": "Hello World",
"body": "post body will contain large text"
"pictures": ["1.jpg", "2.jpg"],
"date": "01/01/1970",
"lastModifiedDate": "01/02/1970"
}
}
For storing multiple comments at once I run a loop times the count of post to push all post in an array and use forkJoin to call the API.
This works but takes a lot of time to finish because forkJoin completes only after all subscriptions have completed.
posts = [
post: {
"userId": 1,
"postId": 1,
"title": "Hello World",
"body": "post body will contain large text"
"pictures": ["1.jpg", "2.jpg"],
"date": "01/01/1970",
"lastModifiedDate": "01/02/1970"
},
post: {...}
]
let list = [];
for(let i=0; i<posts.length; i++){
list.push({postObject});
}
forkJoin(list).subscribe(response => {
console.log(response);
}
2
Answers
You could use
merge
instead offorkJoin
. See doc.You will get the result of each call one by one.
You can create an observable that emits an object that represents the state of your "progress". A simple interface could look like this
We can start the observable stream with the individual requests. I’d use a
Subject
to push single requests through something like this:Then, we can create an observable that does your desired behavior:
To achieve updating the state twice for each request (once upon firing the request, and once when receiving the response) we need an observable that emits twice. We can achieve using the
startWith
operator like this:We use
startWith
to immediately emit a value so we can update the overall state before the response comes back. Notice theisComplete
flag. It’s set tofalse
for the initial emission and set totrue
for the emission that occurs after the response is received. Later on we need a way to determine which property of our state object to update.With this in mind, we can now create our observable based on the
response$
stream. We will use thescan
operator to maintain our state.scan
take two parameters:Each time
scan
receives an emission, it will run the function and emit the result.Explaination:
mergeMap
– will executethis.post()
each time it receives an emission fromrequest$
, then it will emit the response. (rememberthis.post()
emits two values for each request).scan
– receives emissions fromthis.post()
and runs the provided function to calculate a value based on the current emission and prior emissions. We return an object with two propertiestotal
andcompleted
. When the emission is denoted asisCompleted = false
, we increment the total. WhenisCompleted = true
we increment the completed count.Now, you can simply consume the
requests$
observable in the template using theasync
pipe:Here is a StackBlitz example you can play with.