Following a series of Observables in Angular 2+ in a sequential order

Apologies if this question has been answered elsewhere, I attempted to search for it but I'm not exactly sure what I should be looking for.

Imagine I have this complex object:

userRequest: {
    id: number,
    subject: string,
    ...
    orderIds: number[]
    ...
}

order: {
    id: number,
    ...
    clientId: number,
    productIds: number[]
}

client: {
    id: number,
    name: string,
    ...
}

product: {
    id: number,
    name: string,
    price: number
}

At some point, the user will fill out a form using this composite object and submit it for analysis. However, before submission, the data must be validated. Since the user is inputting data received on paper, a request for more information may be necessary if the data is deemed "invalid."

Therefore, validation is required for the request, orders, products, and client. The process involves displaying a "Validating Request" screen and subsequently showing a "Valid" or "Invalid" screen after checking each element.

The challenge arises when handling http requests and Observables. While attempting to understand various operators and their combinations, I find myself completely lost.

Essentially, I receive an Observable<userRequest> from the server initially. Subsequently, upon receiving a userRequest, I need to retrieve all corresponding orders by their IDs. Following this, I must obtain the client and their associated products once an order is obtained.

All these actions occur asynchronously, with dependencies between elements - such as not being able to fetch the client or products until the order is received, and requiring the userRequest for obtaining the orders. Moreover, acquiring both the client and products simultaneously poses further complexity due to their shared dependence on the order. Finally, validation is required for every element (request, order, client, product) before determining the overall validity of the request.

In summary:

  1. I must acquire an Observable<userRequest> and validate it
  2. Next, I need to acquire an Observable<order[]> and validate each individual order
  3. For each order: a) Obtain an Observable<Client> and validate it b) Get an Observable<Product[]> and validate each product
  4. Await completion of all Observables and check for overall validity

Steps 1 and 2 are executed sequentially, whereas step 3.1 and 3.2 are triggered after completing step 2. All subsequent steps must also adhere to this sequential processing.

Although I have a general idea of what needs to be done, I struggle with chaining Observables in a sequential manner due to dependencies. Especially challenging is coordinating validations for the Client and Products that both require the Order ID. Despite numerous attempts, I am unable to grasp the concept entirely.

bygrace - No, I do not intend for validation to block the process. Validation should occur smoothly without hindering the flow, leading to requests for any missing or invalid components which can be displayed at the end. Therefore, I seek a way to determine when all elements have been successfully processed so I can identify any errors present.

The methods for retrieving the request, orders, clients, and products are encapsulated within their respective services which make http requests and return Observables. Thus, synchronizing these calls, especially in the case of Orders where two Observables are needed for the same Order ID, presents a significant challenge.

QuietOran - Below is a snippet of my attempt to solve this issue. However, I acknowledge its shortcomings and recognize that I am currently lost...

onValidateRequest(requestId: number) {

  this.requestService.getUserRequest$(this.requestId)
    .do(request => {
      this.validateRequest(request);
    })
    .concatMap(request => this.orderService.getOrdersForRequest$(request.id))
    .do(orders => {
      this.validateOrders(orders);
    })
    .concatMap(orders => {

      // This section where I get completely stuck
      // Successfully retrieved the request and orders, however, struggling to obtain the client AND products here
      // Need to validate each one upon receiving them
      // Then return something

    })
    .do(() => {
      // When validating an element, any errors encountered are added to an array
      // Upon completion of all above Observables, this function checks for errors
      this.checkForErrors();
    })
    .subscribe();

}

Answer №1

I have provided a rough outline that can be refined with feedback since I am uncertain about the exact format of the desired data. Hopefully, this will guide you in the right direction.

If you need to retrieve data from one observable to pass to another, you can utilize switchmap or a similar method. If you require both the input value and the result together, consider using combineLatest or a similar approach.

console.clear();
function getUserRequest(requestId) {
  return Rx.Observable.of({ id: 1, subject: 'a', orderIds: [10, 20] })
  .delay(500).take(1);
}
function getOrdersForRequest(requestId) {
  return Rx.Observable.of([
    { id: 10, clientId: 100, productIds: [ 1000 ] },
    { id: 20, clientId: 200, productIds: [ 1001, 1002 ] }
  ]).delay(200).take(1);
}
function getClientForOrder(orderId) {
  let client;
  switch(orderId) {
    case 10:
      client = { id: 100, name: 'Bob' };
      break;
    case 20:
      client = { id: 200, name: 'Alice' };
      break;
  }
  return Rx.Observable.of(client).delay(200).take(1);
}
function getProductsForOrder(orderId) {
  let products;
  switch(orderId) {
    case 10:
      products = [{ id: 1000, name: 'p1', price: 1 }];
      break;
    case 20:
      products = [
        { id: 1001, name: 'p1', price: 2 },
      { id: 1002, name: 'p1', price: 3 }
      ];
      break;
  }
  return Rx.Observable.of(products).delay(200).take(1);
}

Rx.Observable.of(1)
  .switchMap(id => Rx.Observable.combineLatest(
    getUserRequest(id),
    getOrdersForRequest(id)
      .switchMap(orders => Rx.Observable.combineLatest(
          Rx.Observable.of(orders),
          Rx.Observable.combineLatest(...orders.map(o => getClientForOrder(o.id))),
          Rx.Observable.combineLatest(...orders.map(o => getProductsForOrder(o.id)))
        )
      ),
    (userRequest, [orders, clients, products]) =>
        ({ userRequest, orders, clients, products })
  )
).subscribe(x => { console.dir(x); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.min.js"></script>

Currently, I have organized the results by category. If you prefer them nested or formatted differently, please provide feedback for further adjustments.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

In search of a way to verify if a string contains a specific word

I'm currently working on a dynamic tooltip feature. My goal is to detect multiline tooltips by checking if my data includes \r. The code snippet below is an example of how I am trying to analyze my description, but so far, I have been unsuccessfu ...

What is the process for obtaining a flattened tuple type from a tuple comprised of nested tuples?

Suppose I have a tuple comprised of additional tuples: type Data = [[3,5,7], [4,9], [0,1,10,9]]; I am looking to develop a utility type called Merge<T> in such a way that Merge<Data> outputs: type MergedData = Merge<Data>; // type Merged ...

Implementing Laravel pagination to retrieve data through ajax calls

I am currently working on setting up a pagination feature using Laravel. When it comes to the backend, I have set up my JSON response in the following way: if(isset($request->myDate)) { $request->validate([ ' ...

How can nested json be sorted effectively based on two specific fields?

Example Data: [{ 'ID': objID(abc123), 'Department': 'IT', 'Employees': [ { 'ID': 3, 'StartDate': '24-12-2022T08:30', 'active': true }, { ...

Sending data from TextBoxFor to controller with @Ajax.ActionLink in MVC

I’ve gone through numerous questions dealing with the same issue, yet none of them seem to solve my problem (or I’m completely missing the point). As the title suggests, I am attempting to transfer the value from a TextBoxFor to my controller using an ...

Angular Compilation Blocked Due to Circular Dependency Error

Currently, I am utilizing WebStorm as my IDE to work on a personal project that I envision turning into a game in the future. The primary goal of this project is to create an Alpha version that I can showcase to potential employers, as I am actively seekin ...

Using Ionic/Angular ion-datetime with specific conditions

In my Ionic app, I have a datetime picker where users can select a time, but with one condition: If the hour is equal to '21', then the minutes must be set to '00' (not '30'). For all other hours, the minutes can be either &ap ...

When I decide to incorporate both BootstrapVue and Vuetify CSS frameworks into a single project

Can I incorporate two CSS frameworks into my current Nuxt.js project? Does anyone have any insight on why it might be beneficial to use two different CSS frameworks in a Vue.js project? Please provide some guidance on this matter. I am looking to optimize ...

TS2531: Potentially null object

I am facing an issue in my React-TypeScript project with the following code snippet. Despite having null checks, I am still getting an error "object is possibly null" while running the app. The error specifically occurs in the last part of the if conditio ...

Error encountered while rendering HTML template with Django and AngularJS due to TemplateSyntaxError

Hello, I am new to Angular and Django. I have built a Django webpage and am attempting to display values from a basic AngularJS app and controller tutorial using my HTML template. However, I keep encountering an error in the index.html file when trying to ...

Using a jQuery dialog to launch a popup window specifically optimized for Safari browsers

I recently encountered an issue with my plain JavaScript function that opens a pop-up window. It functions perfectly in Chrome and Firefox, but I faced difficulty in Safari due to the default popup blocker preventing the page from opening without any err ...

Implementing File Retrieval in a Controller

I have a specific file where I declared the URL for my videouploadfolder, and now I need to reference that file in my JavaScript config.js. var filepath = { uploadVideoUrl : './public/videos' } In my Node.js script, I included it like th ...

What is the best way to eliminate query parameters in NextJS?

My URL is too long with multiple queries, such as /projects/1/&category=Branding&title=Mobile+App&about=Lorem+ipsum+Lorem+. I just want to simplify it to /projects/1/mobile-app. I've been struggling to fix this for a week. While I found so ...

Storing the state of web applications in AJAX applications

How can the application state be maintained across requests for thick JavaScript clients? In addition to client managed cookies and descriptive URLs, are there any other methods? Please note that by clients I am referring to thick JavaScript clients. The ...

A guide to positioning the content of an Angular tag within a span element

Can someone help me figure out how to properly align the PO number and Vendor information on my page? from PO Number: 344 Vendor: yu PO Number: 3445 Vendor: yu PO Number: 344 Vendor: yu to PO Number: 344 Vendor: yu PO Number: 3445 Vendor: yu PO Num ...

Guide to dynamically implementing pagination through AJAX calls

In this javascript code snippet, I have a class named PurchaseHistory. var baseUrl = null; var parameters = null; var currentPageNumber = null; var TotalPages = null; var PageSize = null; $(document).ready(function () { baseUrl = "http://localhost/AP ...

What sets a module apart from a script?

As I delve into the depths of TypeScript documentation to grasp the concept of modules, particularly ES6 modules, I stumbled upon some interesting insights. typescript-modules - this documentation talks about typescript modules and highlights an important ...

Error in refreshing JWPlayer page: Plugin loading failure - File not located

I'm implementing JWPlayer on my Localhost environment. Here's the code snippet I'm using: <div id="Player">loading...</div> <script type="text/javascript"> jwplayer("Player").setup({ file: "<?php echo $video[' ...

Incorporate time zone awareness to JavaScript date objects

Whenever I create objects in my MongoDB using Mongoose, the dates are displayed in the format of, for example, 2016-10-10T15:35:52.764Z (alternatively, it could be yyyy-MM-ddTHH:mm:ssZ). When I utilize Date.now() in JavaScript, I am given a timestamp. Is ...

Modify the appearance of a specific date using ng-pick-datetime

Currently, I am incorporating the Angular Date Time Picker into my frontend design. On the backend, there is a method that determines all the dates that are unavailable for a particular user. I am looking to adjust the color on the calendar to highlight t ...