skip to Main Content

I have an Observable containing an array that i use in the UI with an async pipe to display a table of contracts. One column in that table shows a status value which is ‘not processed’ on page load.

contracts$: Observable<ContractDto[]>

On view init I load the contracts from an API like this.contracts$ = this.api.getContracts()

That works fine, now to my problem:

Via a button click the user can start a process running over all contracts to perform an action per contract. Each action houses an API call and can take 1-3 seconds.

Now i want to update the view to show to the user which contracts are already processed so he gets an indicator on how far the process is. So i want to iterate over the contracts in that observable and update the status field to ‘processed’ when the action has been executed.

But i can’t find a way to iterate over the contracts observable and update this.contracts$ on each iteration. Only thing i could make work was iterating and processing one by one but only update this.contracts$ when all actions have been processed.

my current code looks like this:

// this only gets updated once all contracts have been processed
this.contracts$ = this.contracts$
            .pipe(
                // flatten array of contracts
                concatMap(contract => contract),

                // executing the process one by one; returning the updated contract
                concatMap(contract => this.deploy(contract)),

                // without toArray() i get an error on this.contracts$ assignment because 
                // ContractDto can not be assigned to ContractDto[] - which is correct.
                // and here is my missing link because I want to update this.contracts$ after EACH iteration.
                toArray(),
            )

Many thanks in advance for your time

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @oyinlade-demola 's reply i was able to fix my problem using the scan operator.

    For whoever has a similar problem i just want to post my final working example code:

    this.contracts$ = this.contracts$
        .pipe(
            concatMap(allContracts =>
                from(allContracts).pipe(
                    concatMap(contract => this.deploy(contract)),
                    scan(
                        (contractsAcc, current) => contractsAcc.map(c => c.id === current.id ? current : c), 
                        allContracts
                    ),
                )
            )
        )
    

  2. If you want to update the contracts$ observable after each iteration, you can use the scan operator along with concatMap. The scan operator allows you to accumulate values over time, which is useful for updating the contracts array incrementally.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search