1

I have an array of object and each of its objects needs to be updated in database through a put http request.

[
{id: "3e81731f-e3b4-405d-a5ca-bef52c1b036a", date_created: "2019-07-24T15:55:31.372460Z", date_modified: "2020-07-13T03:25:02.720870Z", einsight_uuid: "d15176ab-ecf8-dba1-e040-10ac316407fa", attributes: {…}},
{id: "4707bff2-8265-456f-a4d4-ca1134d85620", date_created: "2019-07-24T15:55:31.372460Z", date_modified: "2020-07-13T03:25:06.238019Z", einsight_uuid: "fcda4259-3ecb-4566-85b0-5b6d5c7647f6", attributes: {…}}
{id: "d29a3340-04b6-431b-8671-a0f0d25a9b51", date_created: "2019-07-24T15:55:31.3724", date_modified: "2020-07-13T03:25:06.238019Z", einsight_uuid: "fcda4259-3ecb-4566-85b0-5b6d5c7647f6", attributes: {…}}
]

how can i call the api to save each record in array. Calling Api in a loop is an easy option but its not sufficient. i want to keep record of each response. how many of them succeeded or failed . Is there any available known best practice to perform such kind of action in angular or rxjs

2
  • Every time you call your api you will get a observable response, to which you can .subscribe(). Inside of those subscribe callbacks you will have the response for each and every request. Does this fit your requirement? Commented Jul 13, 2020 at 7:01
  • Yea i can do it this way. i can subscribe the response and update user with every records status. So its totally fine to call a put request in loop? There is no performance issue? I was wondering if there is something available for put like we have ForkJoin Commented Jul 13, 2020 at 7:25

3 Answers 3

1

You could do it either in parallel or sequential requests based on the number of requests and your requirement.

Parallel requests

Use RxJS forkJoin function with tap and catchError operators. Try the following

import { forkJoin, of, from } from 'rxjs';
import { tap, catchError, concatMap } from 'rxjs/operators';

putDataParallel() {
  let success = 0;                   // <-- trivial counters
  let errors = 0;

  const reqs = this.urls.map(url =>  // <-- replate `this.urls` with your object array
    this.http.put(url).pipe(         // <-- replace `url` with your own PUT request
      tap(_ => success++),           // <-- count successful responses here
      catchError(err => {        
        errors++;                    // <-- count errors here
        return of(err);              // <-- remember to return an observable from `catchError`
      })
    )
  );

  forkJoin(reqs).subscribe(
    null,                            // <-- you could change response callback to your requirement
    err => console.log(err),
    () => console.log(`Success: ${success}\nErrors: ${errors}`),
  );
}

Sequential requests

Use RxJS from function and concatMap operator for sequential stream of data. Try the following

import { forkJoin, of, from } from 'rxjs';
import { tap, catchError, concatMap } from 'rxjs/operators';

putDataSequential() {
  let success = 0;                      // <-- trivial counters
  let errors = 0;

  from(this.urls).pipe(                 // <-- replate `this.urls` with your object array
    concatMap(url => {
      return this.http.put(url).pipe(   // <-- replace `url` with your own PUT request
        tap(_ => success++),            // <-- count successful responses here
        catchError(err => {        
          errors++;                     // <-- count errors here
          return of(err);               // <-- remember to return an observable from `catchError`
        })
      )
    })
  ).subscribe(
    null,                               // <-- you could change response callback to your requirement
    err => console.log(err),
    () => console.log(`Success: ${success}\nErrors: ${errors}`),
  );
}

Both the methods will run until all the objects in the array are completed regardless of errors or responses. If you however wish to break the sequence in case of an error, replace the return of(err); statement in the catchError operator to throw an error or complete notification instead. For example you could use RxJS EMPTY constant: return EMPTY;.

I've used trivial counters as an example. You could instead use objects (for eg.) to log both the number of response/errors and corresponding input to the HTTP request.

Working example: Stackblitz

Sign up to request clarification or add additional context in comments.

Comments

0

you can use rxjs concat but concat will stop in case observable throw an error so you can use catchError to return a new observable

import { of ,concat} from 'rxjs';
....
 data = [
    {id:1},
    {id:2},
    {id:-1},
    {id:2},

    ]
  constructor(private http:HttpClient){
    let httpReqs = this.data
          .map(i => 
              this.http.put(`https://jsonplaceholder.typicode.com/posts/${i.id}`,{})
              .pipe(catchError(err => of({err})))
              );

     concat(...httpReqs).subscribe(console.log)
    }

demo 🚀🚀

check this 👉 rxjs concat

2 Comments

Thanks for the answer but i dont want to stop it till it finishes for all records.
concat will stop in case an error but I use catchError to return a new observable in that case the next observable will run ,check the demo console
0

As you can see, each item will be processed with concateMap and after making an HTTP request, each item resultarray will be updated. There are multiple ways, you can achieve this result. This is one of the ways shown here.

 items =[
       {id: "3e81731f-e3b4-405d-a5ca-bef52c1b036a", name :'united', result:[]},
       {id: "4707bff2-8265-456f-a4d4-ca1134d85620", name: 'ind', result:[]},
       {id: "d29a3340-04b6-431b-8671-a0f0d25a9b51", name:'ru', result:[]},
       {id: "xxx-xxxx-xxxx-xxxxx", name:'', result:[]}
    ]



from(this.items)
    .pipe(
        concatMap(item => this.ob2(item.name).pipe(
          catchError(err=>of(err)),
          tap(result =>{
            if(result.length){
              item.result = result;
            }else{
              // error scenario can be handled here
            }
            console.log('Updated each item', item)
          })
        ))
    ).subscribe();
  }

DEMO

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.