Child component in Angular displays array as undefined when using parametric route

My goal here is to display a specific property of an item from the jobs array in a child component when navigating to /jobs/:id

Parent Component

export class HomeComponent implements OnInit {

  public jobs!: JobInterface[]

  constructor(
    private jobsService: JobsService
  ) {
  }

  ngOnInit() {
    this.getJobs()
  }


  getJobs(page = 1, filters = {city: 'Rome, Italy', level: ''}): void {
    this.jobsService.getJobs(page, filters)
      .subscribe((response) => {
        this.jobs = response.results.map(job => ({
            id: job.id,
            contents: job.contents,
            name: job.name,
            publication_date: job.publication_date,
            locations: job.locations,
            levels: job.levels,
            company: job.company

          })
        )
      })
  }

}

While the routing works fine, I'm facing an issue in the child component where the jobs array is shown as undefined:

Child component

export class JobDetailsComponent implements OnInit {
  jobId : any
  @Input() jobs! : JobInterface[]
  selectedJob : any
  constructor(private ActivatedRoute : ActivatedRoute) { }

  ngOnInit(): void {
    this.jobId = this.ActivatedRoute.snapshot.paramMap.get('id')
    console.log(this.jobs)
    this.selectedJob = this.jobs.filter(this.jobId)

  }

}

Parent component's HTML

<app-search-bar (OutputFilters)="getFilteredResults($event)" ></app-search-bar>
<app-job-cards [jobs]="jobs"></app-job-cards>
<app-job-details [jobs]="jobs"></app-job-details>

What is the correct way to address this issue? What am I missing in my code?

Answer №1

Due to the changing route, the absence of jobs or job in the job-details component is noticeable. Components are rendered before data retrieval.

To address this issue, consider implementing a flag to indicate when the data is ready for rendering the component. Move the job-details component to the job-cards component and pass a single job through the router:

  • Introduce a flag to specify when the data is ready:
export class HomeComponent implements OnInit {
  dataReady:boolean = false;

// Set to true after receiving the response..
this.dataReady = true;

In the template:

<app-job-cards *ngIf="dataReady" [jobs]="jobs"></app-job-cards>

Next steps include:

  • Removing app-job-details from home-component

Pass the job to the job-details component using the router within the job-cards template:

<a mat-stroked-button routerLink="jobs/{{job.id}}" [state]="job">See More</a>

In the job-details component, access it via the history API:

ngOnInit(): void {
    this.selectedJob = window.history.state;
}

This approach may not be ideal as the direct route might lead to an empty result. Consider storing jobs in the jobs-service instead of the home-component, allowing for shared data between components. Implement methods like getJobs and getJobDetails to manage fetching and processing jobs efficiently.

edit: Sharing Service Data:

Move processing logic from home components to the jobs-service. Create methods that combine fetching and processing, returning jobs as observables. Subscribe to these methods across components to fetch jobs effectively.

Sample implementations:

  • jobs-service
  getJobs(): Observable<JobInterface[]> {

    if(this.jobs?.length > 0) {
      return of(this.jobs);
    }

    return this.fetchJobs();
  }
    

  fetchJobs(page = 1, filters = { city: 'Rome, Italy', level: '' }): Observable<JobInterface[]> {

    const params = new HttpParams({ encoder: new CustomHttpParamsEncoder() })
      .set('category', 'Science and Engineering')
      .set('page', page)
      .set('location', filters.city)
      .set('level', filters.level)

    return  this.http.get<APIResponseInterface>(this.url, { params })
        .pipe(map(response => {

          this.jobs = response.results.map(job => ({
            id: job.id,
            contents: job.contents,
            name: job.name,
            publication_date: job.publication_date,
            locations: job.locations,
            levels: job.levels,
            company: job.company
          })
          )

          return this.jobs;

        })
    );

  }
  • home-component
getJobs(): void {

  this.jobsService.getJobs().subscribe((jobs: JobInterface[]) => {

    this.jobs = jobs;
    this.dataReady = true;

  });
}
  • job-details-component
ngOnInit(): void {

this.jobId = this.ActivatedRoute.snapshot.paramMap.get('id')

this.jobsService.getJobs().subscribe((jobs:JobInterface[]) => {

  this.selectedJob = jobs.find(job=> job.id == this.jobId);
  });
}  

The filter method should directly call jobsService.fetchJobs to modify jobs accordingly.

For more information, explore this example on sharing data between components and consider other solutions provided.

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

Error: The @use directive must come before any other rules in Angular

Error message: Issue: Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): Error Details: HookWebpackError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js) ...

Effective ways to properly utilize the kendo-switch angular component for seamless rendering of the "checked" state

I recently started a new project in Angular 4 using CLI and incorporated various Kendo UI components. However, I encountered an issue with the kendo-switch component where it does not toggle correctly when set to checked=true. Instead of toggling from left ...

Incorporating real-time checked checkbox values into a running list

When displaying a list of preferences as checkboxes, I encountered an issue with the binding part. I am trying to capture the IDs of the checkboxes that are checked. Here is my attempt, which unfortunately does not work: <div class="checkbox" *ngFor="l ...

Tips for compacting JSON in Typescript

I have encountered a challenge in my coding where we are dealing with quite large data objects. I am exploring the possibility of compressing these data objects to reduce payload size. For instance, if our input json size is 2 MB, can it be compressed to a ...

Struggles with deducing argument types in Typescript

I'm struggling to comprehend an inference error. The ts linter highlights (event: E) within useCallback with the following message. When I cast the callback of useCallback with as T, the linter message disappears. Is there a way to avoid this workarou ...

Guide to invoking a REST API endpoint in TypeScript and retrieving the data it returns

In my React SPFx web part, I'm trying to retrieve the current SharePoint Page Title using this API Call: let listTitle: string = this.props.context.pageContext.list.title; let pageItemId: number = this.props.context.pageContext.listItem.id; let url = ...

Issue with Angular Material: Default selection not being applied in mat-select component

When I have a mat-select with options defined as objects in an array, I am facing an issue where the default selected value is not being set when the page renders. In my TypeScript file, I have: public options2 = [ {"id": 1, "name": "a"}, {"id": 2 ...

Using Angular 5 for sending HTTP POST requests with x-www-form-urlencoded content-type

Currently, I have been immersing myself in Angular and am nearing completion of my initial end-to-end application. This specific app is designed to interact with Spotify's public API in order to search for music and play track previews. The primary i ...

Troubleshooting issue of data-binding failure with dynamic component loader in Angular2-universal-started

I am currently utilizing the angular2-universal-starter project. While attempting to pass an object to a child component using @Input, I encountered some issues with it not functioning correctly. I have implemented a dynamic component loader to load the ...

Tips for synchronizing arrays using the Angular Drag-Drop Component

My drag and drop component in HTML looks like this: <div class="example-container flex flex-col text-center h-fit min-h-[10rem] w-[15%] border-2 border-gray-100 rounded-md shadow-md" > <h2 class="text-xl font-semibo ...

JavaScript ECMAScript 6 - WARNING: "Decorators can only be applied to a class when exporting"

In ECMAScript 6, I am attempting to export a function so that I can import it and utilize it in other files for the sake of writing DRY code. However, an error message is appearing: You can only use decorators on an export when exporting a class (16:0) ...

Displaying dynamic key-value pairs in each row of an Angular mat-table

I need help displaying a key-value pair data in JSON format dynamically within a table using Angular mat-table. The keys will vary, so there is no set list of keys that will be included in the JSON. This is an example of the data: var data = { "cars" : 2 ...

moving the array generated on the HTML page to a different page

Can anyone help me with transferring an array from one page to another? I have a list of names in the code below and I want to be able to access and print this array on another page using JavaScript. Your assistance is greatly appreciated! <html> ...

transformed an Angular 2 web application into a sleek and functional mobile application

I am looking to convert my basic angular2 web application into a mobile app using cordova. Is there a way to achieve this without relying on Ionic or nativeScript? ...

Ways to prevent Firebase from issuing a warning about my use of the development version

Currently, I am developing a login page for my ReactJS application utilizing the firebase authentication package. Within my global firebase file, I have successfully imported the necessary packages: import firebase from 'firebase/app'; import & ...

Having trouble halting the execution at specific checkpoints within an asp.net core project containing an angular 8.0 application located in a subfolder named ClientApp

Encountering difficulties stopping at breakpoints in an asp.net core project with an angular 8.0 app located in a subfolder within ClientApp. The app I need to set a breakpoint in is located at clientapp\apps\microsympan\app\src\ap ...

What is the definition of a non-arrow React functional component in TypeScript?

Defining types for a React functional component in TypeScript can be done like this: export const Component: React.FC = () => { return // Content }; But how would you define the types for a non-arrow function? function Component() { return // Con ...

Commit to choosing an option from a dropdown menu using TypeScript

I just started learning about typescript and I have been trying to create a promise that will select options from a drop down based on text input. However, my current approach doesn't seem to be working as expected: case 'SelectFromList': ...

Whenever I attempt to host my Node.js app using the GCP deploy command, it fails to work properly. The error message that appears states: "Module 'express' cannot be found."

My NodeJS application is written in TypeScript and utilizes the Express framework. I'm looking to host it on the GCP cloud using the gcloud app deploy command. First, I compile my TS sources to JavaScript - is this the correct approach? Afterwards, I ...

Pagination in Firestore with AngularFire2 allows users to query items based on a specified range, utilizing the method .startAfter(lastVisible

Within a component, I am looking to retrieve a specific range of items from FireStore, such as from 0 to 5 or from 5 to 10. While exploring FireStore's documentation, I came across this but it doesn't mention AngularFire2. Despite several attempt ...