The parameter failed to initialize causing the HTTP service to fire prematurely

In my project, I am utilizing Angular 10. Within the ngOnInit function, I have nested HTTP calls structured like this:

ngOnInit(): void {
  
  let communetyid;
  
    this.route.data.subscribe(data => { 
      this.route.params.subscribe(params => { 
        
      if(data.category === "code")
      {
        this.dataService.getCode(params.code)
          .subscribe(resp => { communetyid = resp.results.communityId });
      }
      else
      {
        communetyid = params.id
      }

      this.dataService.getCommunityById(communetyid)
        .subscribe(response => { this.community = response.results; 
        
            ///other http calls
        
       }) 
     })
   })
})

The issue arises where dataService.getCommunityById is executed before the value of communetyid is set due to asynchronous operations, resulting in an error message in the console.

Is there a way to modify the code so that when dataService.getCommunityById is called, the value of communetyid is initialized?

I understand that one approach would be to include dataService.getCommunityById within the subscribe method of dataService.getCode, but I am looking for a solution that avoids duplicating code.

Answer №1

To maintain the original pattern of nesting subscriptions, simply relocate the "getCommunityById" HTTP call within the callback of "dataService.getCode." The crucial point to grasp here is that "dataService.getCode" will provide an observable (data not yet available), while your else statement functions with a value that already exists (params.id). The correct approach to resolving this issue involves utilizing concatMap() within a pipe() and chaining these operations together. Within the concatMap() callback, manage your if-else branch logic by returning either this.dataService.getCode or Observable.of(params.id). Subsequently, in the following concatMap(), use this value as an argument for this.dataService.getCommunityById.

Answer №2

Consider enhancing your Observables by using advanced operators

export class ChildComponent implements OnInit {
  constructor(
    private route: ActivatedRoute,
    private dataService: DataService
  ) {}

  // Extracting Parameters from the Activated Route
  params$ = this.route.paramMap.pipe(
    map(params => ({
      id: params.get("id"),
      code: params.get("code"),
      category: params.get("category")
    }))
  );

  // Retrieve Community Id
  communityId$ = this.params$.pipe(
    mergeMap(({ category, code, id }) =>
      category === "code"
        ? this.dataService
            .getCode(code)
            .pipe(map(({ results }) => results.communityId))
        : of(id)
    )
  );

  // Get Community Information
  community$ = this.communityId$.pipe(
    switchMap((communetyId) => this.dataService.getCommunityById(communetyId))
  )

  ngOnInit() {
    this.community$.subscribe({
      next: console.log
    });
  }
}

Explanation of the Code

Parameter Extraction

  params$ = this.route.paramMap.pipe(
    map(params => ({
      id: params.get("id"),
      code: params.get("code"),
      category: params.get("category")
    }))
  );

The paramMap method is used to extract parameters from the ActivatedRoute. According to Angular Docs, the params method is either deprecated or will be deprecated in the future.

ActivatedRoute contains two properties that are less capable than their replacements and may be deprecated in a future Angular version.

In the next step, we define a property called communityId, which depends on mapping the params while checking the value of the category property. The mergeMap operator is used here to combine the subscriptions of params$ and communityId$.

Lastly, we obtain information about the community with the use of switchMap. This operator ensures that we do not continue with an initial request if a new request has been made, making it the most suitable choice for this scenario.

View a demo example here

Answer №3

Revise your code as shown below:-

ngOnInit(): void  {
  
  let communityId;
  
    this.route.data.subscribe(data => { 
      this.route.params.subscribe(params => { 
        
      const communityIdObs = date.category === 'code' ? this.dataService.getCode(params.code).pipe(map(resp => resp.results.communityId)) : of(params.id);
      communityIdObs.pipe(mergeMap(res => {
         return this.dataService.getCommunityById(communityId);
      }).subscribe(response => { 
            this.community = response.results; 
            ///other http calls
      });
})

The merger operator ensures that your calls are executed in sequence and I have also made the code shorter.

Answer №4

If you're looking to streamline your code, consider leveraging the concatMap Operator.

The concatMap Operator is perfect for transforming one response into parameters needed for the next request in line.

When using the concatMap() Operator, it internally subscribes to the Observable returned from its projection function and patiently waits for it to complete before re-emitting all of its values.

Check out this practical example below:

function simulateHTTPRequest(url) {
    return Observable.of(`Response from ${url}`)
      .delay(1000);
}

let urls = ['url-1', 'url-2', 'url-3', 'url-4'];
let startTime = (new Date()).getTime();

Observable.from(urls)
  .concatMap(url => simulateHTTPRequest(url))
  .timestamp()
  .map(timestampData => [timestampData.timestamp - startTime, timestampData.value])
  .subscribe(data => console.log(data));

Answer №5

Although rxjs can be used for chaining observables, there is a simpler approach to resolve the issue at hand. By moving anything inside the subscription of an observable outside as a separate function, you can prevent unnecessary calls to your dataService.getCommunityById function.

ngOnInit(): void  {

let communityId;

this.route.data.subscribe(data => { 
  this.route.params.subscribe(params => { 
    
  if(data.category === "code")
  {
    this.dataService.getCode(params.code)
      .subscribe((resp) => 
      { 
          communityId = resp.results.communityId;
          getCommunityData(communityId);
    });
  }
  else  
  {
    communityId = params.id;
    getCommunityData(communityId);
  }

  })
 })  
})

getCommunityData(communityId) {
  this.dataService.getCommunityById(communityId)
  .subscribe(response => { 
     this.community = response.results;
     //other http calls
  })
}

This method offers an alternative to using rxjs and is easier to comprehend. Additionally, rxjs operators like concatMap and of can also be utilized, as suggested by Medhat Mahmoud and Muhammet Can TONBUL.

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

Simultaneous asynchronous access to a single object

I have been working with JS/TS for quite some time now, but the concept of race conditions still perplexes me. I am currently attempting to execute the following logic: class Deployer { protected txDataList: Array<TXData>; constructor() { this ...

Detecting when Angular2 input values change

Here is the updated code snippet: I am attempting to modify an input field and then submit the form. However, when I retrieve the form data using server-side code, the input has not been updated. <form id="cardsForm" method="post"> <inpu ...

nodemon and ts-node not working as expected, failing to automatically recompile

I have been working on creating a REST API using express+ts-node. Following various online tutorials, I managed to set everything up and when I run the app using npm run dev, it works perfectly fine. However, I am facing an issue where it is not automatica ...

Utilizing Angular 8 (TypeScript) and .NET Core to send a GET request with dates as a parameter

I attempted to send 2 dates as parameters in the GET request, but I am only receiving this: Model public class Dates { public DateTime from { get; set; } public DateTime to { get; set; } } .net core [HttpGet] [Route("main/exportToEx ...

The attribute 'finally' is not found on the data type 'Promise<void>'

I've been attempting to implement the finally method on a promise but continue running into this issue. Property 'finally' does not exist on type 'Promise<void>'. After researching similar problems, I found suggestions to a ...

React Routing: Unleashing the Power of Multi-Level Routing

In my quest to create a route with multiple levels (<Route path="/hello/world" element={<a>hello world</a>} />), I encountered a few issues. Here are the versions I am using: react: 18.1 react-router-dom: 6.3.0 Success with O ...

Inefficiency in POST method prevents data transmission to MongoDB

I've developed a MERN application and now I'm testing the backend using the REST client vscode extension. This is how it looks: `POST http://localhost:4000/signup Content-Type: application/json { "email": "<a href="/cdn-cgi ...

Having trouble with Typescript module path resolution for .js files?

I have embarked on a project in React and I am eager to begin transitioning the js files to typescript. The setup for aliases seems to function smoothly when importing .tsx within another .tsx file, however, it encounters issues when attempting to import . ...

What are the methods to determine the cause of ESLint's slow performance?

Looking to analyze the performance of ESLint in my application. So far, I have only come across one profiling tool provided by ESLint which is the TIMING=1 environment variable. Combining this with DEBUG=eslint:cli-engine allows me to see timing results pe ...

The transparency of Angular Primeng sidebar sets it apart from the rest

I'm currently working on a project using Angular 7 and PrimeNg v7. I am trying to implement the PrimeNg sidebar module, but I am facing issues with the background being transparent and the shadow not appearing when the sidebar is open: Here is what I ...

Leveraging the power of the map function to manipulate data retrieved

I am working on a nextjs app that uses typescript and a Strapi backend with graphql. My goal is to fetch the graphql data from strapi and display it in the react app, specifically a list of font names. In my react code, I have a query that works in the p ...

Submit file in Cypress while hiding input[type="file"] from DOM

Currently, I am conducting end-to-end testing on an Angular project that utilizes Ant Design (NG-ZORRO). In this project, there is a button with the class nz-button that, when clicked, opens the file explorer just like an input type="file" element would. W ...

Exploring the world of asynchronous operations with React Hooks

Hello, I am a beginner when it comes to React and JavaScript. I could really use some assistance with two hooks that I have created: useSaveStorage and useGetStorage. The issue I am facing is that my app is supposed to receive data and save it into AsyncS ...

Finding the row index in an Angular material table

How can I retrieve the row index in an Angular material table? <td mat-cell *matCellDef="let row"> <mat-checkbox (click)="$event.stopPropagation()&quo ...

the process of accessing information from a service in an Angular Typescript file

After making a POST request using Angular's HTTP client, the response data can be accessed within the service. However, is there a way to access this data in the app.component.ts file? I am able to retrieve the response data within the service, but I ...

What is the official name of the key type for the Built-in Object?

There was a built-in type that I used in the past which represented the union of all possible object keys. It was named objectKey or something similar. Here is an example: type objectKey = string | number | symbol Unfortunately, I am drawing a blank on t ...

Develop a directive for transforming data

In my latest project, I am looking to develop a LoaderDirective that can fetch an observable, display a spinner while loading the data, and then switch to showing the actual data once loaded. I also want it to expose the loaded data using the 'as&apos ...

Angular deep linking in an Express server is a powerful combination that allows

I'm developing a single page application using Express and Angular. One feature involves sending users an email with a link to reset their password (https://[domain].com/reset-password/[token]). However, when the user clicks on this link, it redirect ...

Step-by-step guide on deploying your Nestjs API on Google App Engine

Encountering a hurdle while trying to deploy my nestjs api on Google App Engine has left me puzzled. Despite initializing my google cloud project with the google sdk, an error thwarted my progress. To tackle this challenge, I made key adjustments in my cod ...

Dynamically render a nested component, created within the parent component, using a directive

Can a nested component be dynamically rendered as a directive within the parent component? Instead of using the standard approach like this: <svg> <svg:g skick-back-drop-item /> </svg> where "skick-back-drop-item" is the s ...