Send an API request using an Angular interceptor before making another call

Within my application, there are multiple forms that generate JSON objects with varying structures, including nested objects and arrays at different levels. These forms also support file uploads, storing URLs for downloading, names, and other information within the object.

Currently, I convert files to base64 strings and upload them to the backend before making any requests that involve files.

My goal is to enhance this process by waiting for the file upload API call to complete before modifying the user's request body and proceeding with the main post request. However, it seems that these operations are not sequential as intended, as the file gets uploaded in the backend but the user's object remains unmodified. Additionally, the file upload query appears to be executed multiple times without cause.

export class FilesCheckerInterceptor implements HttpInterceptor {

  constructor(private filesService: FilesService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const data = request.body;
    if (data) {
      
      const uploaded: File[] = [];
      this.traverse(data, files => {
        files.forEach(file => {
          const base64 = file.data;
          const result = this.filesService.toFile(base64, file.name);
          uploaded.push(result);
        });
      });
      
      console.log(request);

      return this.filesService.uploadFile(uploaded).pipe(
        mergeMap(response => {
          this.traverse(data, files => {
            for (let i = 0; i < response.length; i++) {
              files[i] = response[i];
            }
          });
  
          return next.handle(request.clone({ body: data }));
        }),
      );      
    }
    else {      
      return next.handle(request);
    }
  }

  traverse(obj: any, action: (value: InternalFile[]) => void) {
    if (obj !== null && typeof obj === 'object') {
      Object.entries(obj).forEach(([key, value]) => {
        if (key === 'attachments' && Array.isArray(value)) {
          // const files = value as InternalFile[];
          action(value);
        }
        else {
          this.traverse(value, action);
        }
      })
    }
  }
}

Answer №1

If you're looking for a solution, consider implementing forkJoin as a viable option.

With forkJoin, it will hold off until all specified observables have emitted and completed before emitting an array or object with the final values from each observable. This operator is particularly useful when dealing with a set of observables where only the last emitted value matters. A common scenario would be making multiple requests upon page load (or another event) and wanting to act only after receiving responses for all requests. In this sense, it operates similarly to Promise.all.

Alternatively, utilizing async-await is also an option.

To delve deeper into forkJoin, check out this resource

Answer №2

After trying various approaches, I stumbled upon this solution which miraculously worked. I have no idea how or why it works, but through trial and error, I managed to achieve the desired outcome.

This snippet should be inserted into the return statement of the original code.

const observable = this.filesService.uploadFile(uploaded).pipe(
  concatMap((response) => {
    //replace current representations with server-given representations of files
    this.traverse(data, (files) => {
      files.forEach((_, index) => {
        files[index] = response[index];
      });
    });

    //make actual request
    const clone = request.clone({ body: data });
    return next.handle(clone);
  })
);

return observable;

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

Issues with the modal controller dialog functionality in Ionic 4

I have been using a modal Controller dialog in Ionic 4 to retrieve parameters from another page. I have coded everything correctly and when I click on the button to launch the modal Controller, it doesn't show up. I have added the modal page in declar ...

Struggling to incorporate generics into a Typescript method without sacrificing the typing of object keys

Currently, I am working on a method in Typescript that is responsible for extracting allowable property types from an object of a constrained generic type. The scenario involves a type called ParticipantBase which consists of properties like first: string ...

Creating HTML elements with dynamic `routerLink` attributes in Angular 2

I have a model that references itself, as shown below. export class Entity { constructor(public id: number,public name: string,public children: Entity[]) { } } My goal is to create a tree list where each item has a routerlink. To achieve this, I ...

Troubleshooting: HTTP client post request working with body.set but not with formData.append for sending data to API

I am in the process of updating the UX for an older application with APIs developed in ASP.NET When I make a POST request as shown below, everything works perfectly. The data is received: var APIURL = sessionStorage.getItem('endpoint') + "/ ...

Top method for verifying input during keyup or blur events

When it comes to validating user inputs, I often find myself wondering about the best approach to take. In this case, I have created a regex for numbers with decimal points. .ts part checkIsNumber(event) { console.log('event', event.target. ...

VScode has identified potential problems with modules that utilize Angular Ivy, however, this should not cause any major issues

Encountering a problem with using Angular in VSCode, where there seems to be no ngModules due to AngularIvy. The error message indicates an issue with 'CommonModule' not being recognized as an NgModule class: [{ "resource": "[...]src/app/com ...

Jest tests are failing because React is not defined

I am attempting to implement unit tests using Jest and React Testing Library in my code. However, I have encountered an issue where the tests are failing due to the React variable being undefined. Below is my configuration: const { pathsToModuleNameMapper ...

Stop modal from closing in the presence of an error

My approach involves using a generic method where, upon adding a food item, a modal window with a form opens for the user to input their details. However, since backend validation for duplicate items can only be retrieved after the API call completes. I w ...

Navigate across list items using the tab key in Angular 7

Below is the sidenav menu snippet containing various items: <ul class="account-settings-container" *ngIf="isUserActive"> <li> <app-account-information></app-account-information> </li> <li> <app-theme-p ...

Implement the addition of subcollections to a unified identification document in Firestore

I am struggling to store the URLs of multiple images (previously saved in Storage) in a subcollection of a Firestore document. Although I have managed to do it, each image generates a new document with its corresponding sub collection img, which is not my ...

Building a PathString Tree

I encountered a similar issue like the one discussed in this post (Get a tree like structure out of path string). I attempted to implement the suggested solution but I am facing difficulties getting it to work within an Angular context. The concept involv ...

The issue of the Angular service being consistently undefined arises when it is invoked within an

I have already researched numerous other SO questions, but none of the solutions worked for me. My goal is to implement an async validator that checks if a entered username already exists. However, every time I type a letter into the input field, I encoun ...

Ways to invoke main.ts to communicate with an Angular component using Electron

I have a good understanding of how to communicate between an angular app and the electron main thread using IPC. However, in my current scenario, I have threads running in the electron main thread for video processing. After these threads complete their t ...

The art of combining Angular 6 with CSS styling for dynamic

Can we dynamically set a value in an scss file from the ts component like demonstrated below? public display: "none" | "block"; ngOnInit(): void { this.display = "none"; } ::ng-deep #clear { display: {{display}} !imp ...

The Angular Behaviour Subject is failing to accurately represent the change status within the component

I am currently working on a MEAN project that involves an Angular Behaviour Subject emitting the status of the logged-in user. Issue: After the user logs in, the app component detects the change in user status. However, the header component (which is part ...

Dependency injection in Angular 2 service not functioning as expected

I am facing an issue while trying to retrieve static data from UserService in Angular 2. Although everything seems correct based on the documentation, it is not functioning as expected. Below is my UserComponent.ts import {Component ,OnInit } from ' ...

Ways to determine if a date matches today's date within a component template

I am currently displaying a list of news articles on the webpage and I want to show the word "Today" if the news article's date is equal to today's date. Otherwise, I want to display the full date on the page. Is there a way to compare the news.D ...

Guide to combining an Angular 2 (angular-cli) application with Sails.js

I am looking to integrate the app created using angular-cli with Sails.js. My background is in PHP, so I am new to both of these frameworks. What are the steps involved in setting them up together? How can I execute commands like ng serve and/or sails li ...

Using the Capacitor plugin for Firebase Auth, log in to Azure AD B2C with Ionic/Angular

I have been trying to authenticate with Microsoft using a Capacitor plugin, but I haven't had any success so far. I carefully followed the instructions in this documentation and made sure everything is well configured. Could you please guide me on w ...

Tips for setting up a system where PHP serves as the backend and Angular acts as the

I am working on a project that utilizes Angular as the front end and PHP as the back end. Both are installed in separate domains, with the PHP project fully completed and operational. I have created an API in PHP which I plan to call from Angular. My ques ...