1

I have a form where I take name as input and count(number).

I want to append the number to the name. For eg.

if user enters worker and count 5. I want to append the number starting from 1 until the count i.e. 5 and it will be like worker-1, worker-2, worker-3, worker-4, worker-5.

I want to then send multiple requests to the server to add these workers in the database.

I am doing something like this currently.

addItems() {

    for (let i = 1; i <= this.count; i++) {
        body.name = this.createItemForm.get('name').value + '-' + i;
        this.add(body);
      }

 }

add(body: formBody) {
    this.cvmService.createServer(body).subscribe(() => {
      this.alertify.success('New item was successfully created');
      this.close(true);
    }, error => {
      this.alertify.error('\n' + error);
    });
  }

My question is how can I do it with rxjs and which is the best operator to do it. Especially the part where I call add() inside for loop, is there a better way to do it?

4 Answers 4

1

generate is your friend:

const baseName = this.createItemForm.get('name').value + '-';

generate(1, x => x <= this.count, x => ++x).pipe(
  map(val => baseName + val),
).subscribe(bodyName => {
 // do something with your worker-1 .... worker-count
});

generate will generate sequence from 1 up to your input count.

map will just concat your prefix (e.g. 'worker') with the number to produce bodyName.

...and then it is up to you. It is not clear from the code what your body object looks like and what action/s you want to do with each request and what action in the end.

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

2 Comments

can I do something about the part where I call this.add() inside the for loop. instead of that is there a way to better make the service call?
See Rafi Henig reply, it is better than mine, actually.
1

Simply use range operator, it accepts two parameters; start & count (range(start: number, count: number): Observable) and returns an Observable that emits a sequence of numbers which you can transform according to your need.

Consider the following example:

interface Body {
  name: string
}

class Component {
  constructor(
    private alertify: Alertify,
    private cvmService: CvmService
  ) { }

  public addMany(count: number, base: string): void {
    return range(1, count).pipe(

     /**
      * transform each emitted number into a @Body
      */

      map(number => { 
        const body: Body = { name: `${base}${number}` };
        return body;
      }),

      toArray(), // <-- collect emitted values

     /**
      * transform the array of @Body[] returned by @toArray() into an 
      * array of Observables and use @forkJoik operator to wait for all
      * Observables to complete
      */

      switchMap(array => forkJoin(array.map(body => this.addOne(body))))
    ).subscribe(() => console.log("done!"))
  }


  /**
  * transform each body into an Observable 
  * without subscribing to it
  * use @tap operator to listen for events that will be raised
  * by future subscriptions
  */

  public addOne(body: Body): Observable<void> {
    return this.cvmService.createServer(body)
      .pipe(
        tap({
          next: () => this.alertify.success('New item was successfully created'),
          error: error => this.alertify.error('\n' + error)
        })
      )
  }
}

Comments

0

Try this. For example :

forkJoin([res1, res2, res3, res4, res5])
.pipe(map(data => data.reduce((result,arr)=>[...result,...arr],[])))
.subscribe(data =>{

  this.autoCompleteValues = data;
  console.log(this.autoCompleteValues);
});

2 Comments

Could you please explain how I can make use of fork join with my code. I understand but how can I exactly send my values in res1, res2,...
The problem here is that OP does not want always 5 requests. It is dynamic, taken from user input.
0

mergeMap to Turn Values into Subscribed Observables

mergeMap(value => observable) will subscribe and emit the value of the mapped observables for you. It looks like all you need is mergeMap(body => service(body)) and you're off to the races.


The following is a pretty direct translation of what you're doing with RxJS. If your code works, then (in theory, I can't test it) this or something very similar should work.

I'm not sure where body is defined in your example, so I'll pretend it's an object with some properties and you're adding another property (name) to it

addItems(){
  
  range(1, this.count).pipe(
    map(i => ({
      ...body,
      name: `${this.createItemForm.get('name').value}-${i}`
    })),
    mergeMap(body => this.cvmService.createServer(body))
  ).subscribe({
    next: resp => {
      this.alertify.success('New item was successfully created');
      this.close(true);
    },
    error: err => this.alertify.error('\n' + err),
    complete: () => console.log(`${this.count} items where succesfully created`)
  });
  
}

forkJoin to Turn Array of Observables into Array of Responses

The same thing can be done with forkJoin since it looks like your service calls emit once and complete.

Here, you first need to create an array of the service calls you want. I do this by mapping an array [1,2,3,4,5] into body objects [{name:worker-1},{name:worker-2},{name:worker-3},{name:worker-4},{name:worker-5}] and then mapping body objects into service calls [observable,observable,observable,observable,observable].

All that is done without any RxJS, the RxJS part is the forkJoin([observable,observable,...]). Which subscribes to every observables and wait for them all to complete.

The difference is that forkJoin will emit the final results as an array. If you still want an alert and a call to this.close(true) for each result, then we'd need to instrument each service call (source observable in the array) to do that.

When you subscribe, the next callback will be given an array. The implementation looks like this:

addItems(){
  
  forkJoin(
    Array.from(
      {length: this.count}, 
      (_,i) => i+1
    ).map(i => ({
      ...body,
      name: `${this.createItemForm.get('name').value}-${i}`
    })).map(body => 
      this.cvmService.createServer(body).pipe(
        tap(_ => {
          this.alertify.success('New item was successfully created');
          this.close(true);
        })
      )
    )
  ).subscribe({
    next: resp => {
      console.log(`${resp.length} items where succesfully created`);
    },
    error: err => this.alertify.error('\n' + err)
  });
  
}

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.