Generate an array of objects by combining three separate arrays of objects

There are 3 private methods in my Angular component that return arrays of objects.

I want to combine these arrays into one array containing all the objects, as they all have the same class.

Here is the object structure:

 export class TimelineItemDto {
    id: any;
    creatorAvatarUrl: string;
    categoryName: string;
    creatorName: string;
    subcategoryName: string;
    description: string;
    type: string;
}

Below is the code for the component:

export class HomeComponent implements OnInit {

  constructor(private _router: Router, private http: HttpClient) { }

  color: ThemePalette = 'primary';
  classes: TimelineItemDto[] = [];
  requests: TimelineItemDto[] = [];
  courses: TimelineItemDto[] = [];
  timelineItems: TimelineItemDto[] = [];
  checked = false;
  disabled = false;

  ngOnInit(): void {
    const token = localStorage.getItem('jwt');
    if (!token) {
      this._router.navigate(['/main/login']);
    }

    this.getTimelineItems();
  }

  getCourses(): any {
    return this.http
      .get(environment.baseUrl + '/Course/GetCourses')
      .subscribe((data: TimelineItemDto[]) => {
        return data;
      });
  }

  getClasses(): any {
    return this.http
      .get(environment.baseUrl + '/Class/GetClasses')
      .subscribe((data: TimelineItemDto[]) => {
        return data;
      });
  }

  getRequest(): any {
    return this.http
      .get(environment.baseUrl + '/Requests/GetRequests')
      .subscribe((data: TimelineItemDto[]) => {
        return data;
      });
  }

  getTimelineItems(): any {
    var courses = this.getCourses();
    var classes = this.getClasses();
    var requests = this.getRequest();
    this.timelineItems = [...classes, ...courses, ...requests];
    console.log(this.timelineItems);
  }
}

At this line

this.timelineItems = [...classes, ...courses, ...requests];
, I encounter the following error:

core.js:4197 ERROR TypeError: classes is not iterable

How can I resolve this issue?

Answer №1

The Issue

Let's examine the code snippet below

  getCourses(): any {
    return this.http
      .get(environment.baseUrl + '/Course/GetCourses')
      .subscribe((data: TimelineItemDto[]) => {
        return data;
      });
  }

The code above utilizes the .get() method followed by the .subscription() method. This reveals that the function actually yields a subscription rather than an Observable. Therefore, attempting to iterate over this subscription results in an error.

Resolution

To address this issue, there are multiple approaches available. Below is my proposed solution:

  • Fetch classes as Observable
  • Retrieve requests as Observable
  • Obtain courses as Observable
  • Merge these 3 Observables into a single Observable
  • Subscribe to the new Observable

Refer to the following revised code

   constructor(private _router: Router, private http: HttpClient) {}
  color: ThemePalette = "primary";
  timelineItems: TimelineItemDto[] = []
  getCourses = () =>
    this.http.get<TimelineItemDto[]>(
      environment.baseUrl + "/Course/GetCourses"
    );

  getClasses = () =>
    this.http.get<TimelineItemDto[]>(environment.baseUrl + "/Class/GetClasses");

  getRequest = () =>
    this.http.get<TimelineItemDto[]>(
      environment.baseUrl + "/Requests/GetRequests"
    );

  classes$: Observable<TimelineItemDto[]> = this.getClasses();
  requests$: Observable<TimelineItemDto[]> = this.getRequest();
  courses$: Observable<TimelineItemDto[]> = this.getCourses();
  timelineItems$: Observable<TimelineItemDto[]> = combineLatest([
    this.classes$,
    this.courses$,
    this.requests$
  ]).pipe(
    map(([classes, courses, requests]) => [...classes, ...courses, ...requests])
  );
  checked = false;
  disabled = false;
  ngOnInit(): void {
    const token = localStorage.getItem("jwt");
    if (!token) {
      this._router.navigate(["/main/login"]);
    }

    this.getTimelineItems();
  }

  getTimelineItems(): any {
    this.timelineItems$.subscribe({
      next: (items) => this.timelineItems = items
    })
  }

View this solution on stackblitz

Answer №2

To understand how asynchronous data operates, it is essential to refer to this source for detailed information on async data handling.

In essence, one must await the emission of data by the source. In this scenario, waiting for the RxJS observables to emit values before attempting to assign them is crucial. Moreover, when subscribing to multiple observables, utilizing the RxJS forkJoin function can trigger requests concurrently.

export class HomeComponent implements OnInit {
  constructor(private _router: Router, private http: HttpClient) { }

  color: ThemePalette = 'primary';
  classes: TimelineItemDto[] = [];
  requests: TimelineItemDto[] = [];
  courses: TimelineItemDto[] = [];
  timelineItems: TimelineItemDto[] = [];

  checked = false;
  disabled = false;
  ngOnInit(): void {
    const token = localStorage.getItem('jwt');
    if (!token) {
      this._router.navigate(['/main/login']);
    }

    this.getTimelineItems();
  }

  getTimelineItems(): any {
    forkJoin(
      <Observable<TimelineItemDto[]>>this.http.get(environment.baseUrl + '/Class/GetClasses'),
      <Observable<TimelineItemDto[]>>this.http.get(environment.baseUrl + '/Course/GetCourses'),
      <Observable<TimelineItemDto[]>>this.http.get(environment.baseUrl + '/Requests/GetRequests')
    ).subscribe({
      next: ([classes, courses, requests]) => {
        this.classes = classes;
        this.courses = courses;
        this.requests = requests;
        this.timelineItems = [...classes, ...courses, ...requests];
        console.log(this.timelineItems);
      },
      error: error => {
        console.log('handle error');
      }
    });
  }
}

It is advisable to review the aforementioned link. The variable this.timelineItems may remain empty when accessed outside the subscription since it might not have been assigned values yet.

In simple terms, this.timelineItems is only accessible within the subscription scope.

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

Guide to transferring parameters from one function to another in Javascript

For my automation project using Protractor and Cucumber, I have encountered a scenario where I need to use the output of Function A in Function B. While I was able to do this without Cucumber by extending the function with "then", I am facing difficulties ...

Dygraphs: Utilizing two y-axes in a single series

I'm interested in plotting a single series with two synchronized y-axes that display different units, such as °F and °C temperatures. I want the data to be readable from both axes, but only one set of points should be plotted. Here are some approac ...

What is the best way to manage the back button using jQuery?

I'm currently facing a challenge when it comes to managing the Browser's History. While plugins like History.js can be helpful for smaller tasks, I find myself struggling with more complex scenarios. Let me provide an example: Imagine I have a m ...

Angular2 form builder generating dynamic forms based on results of asynchronous calls

When creating my form, I encountered a challenge with passing the results of an asynchronous call to the form builder. This is what I have attempted: export class PerformInspectionPage implements OnInit { checklists: any; inspectionform: FormGroup; n ...

Using renderProps in combination with TypeScript

I've encountered an issue while trying to convert my React project to TypeScript, specifically with the login component that uses react-google-login. The error I'm facing is related to renderProps: Overload 1 of 2, '(props: { component: El ...

Setting a radio button as default based on a variable value can be accomplished by following a few

I am working with 2 radio buttons and I need to dynamically check one based on a variable value. <input type="radio" name="team" id="team_7" value="7"> <input type="radio" name="team" id="team_8" value="8"> The variable number is set dependin ...

What is the reason for the failure of this react ternary return statement?

My slideboard is set up to show a warning component (currently just a "test" div) when the prop "columnsItem" exceeds 50. Everything works fine, but when I switch back to a slideboard with fewer columns, all I see is a blank white screen. Can you help me ...

Challenges with Nested Reactive Form in Angular 4

I am utilizing Angular 4 reactive to develop nested forms. The structure of my forms is as follows: Form Group (userform) FormArray (users) FormGroup (idx) In the complete table, it falls under the Form Group. Each table row is a part of FormArray ...

The UNHANDLEDREJECTION callback was triggered prematurely during asynchronous parallel execution

Asynchronously using parallel to retrieve data from the database in parallel. Upon each task returning the data, it is stored in a local object. In index.js, the cacheService.js is called. Inside cacheService.js, data is loaded from both MySQL and MongoDB ...

How to transfer data from JavaScript to PHP using AJAX

After spending countless hours attempting to make this function properly, I have come to you for assistance :) I have created a PHP page that can exhibit files from a server. I am able to modify the files using an editor plugin, where the Textarea tag is ...

I'm seeking an easy method to adjust the x and y coordinates of a popup rectangle with a fixed size. Can anyone provide

Is there a way to create a simple pop-up rectangle, possibly using jQuery or a similar tool, that presents a scaled-down canvas view of a larger browser window (1000px x 1600px), allowing users to click and determine the x/y position within the full window ...

Personalized Carousel using Ng-Bootstrap, showcasing image and description data fields

I have been working on customizing an Angular Bootstrap Carousel and have managed to successfully change the layout. I now have two columns - with the image on the right and text along with custom arrows on the left. My goal is twofold: First, I am lookin ...

I am having an issue with my registration form in node.js where it does not redirect after submitting

I am currently working on implementing a registration form using node/express for my website. The routes are all set up and the database connection is established. However, I am encountering some issues when users try to submit the registration form on th ...

underscore's _.each() method for callback functions

I've been struggling with implementing my custom _.each() function within another function and keep encountering the issue of getting "undefined" returned. My goal is to utilize _.each() to apply a test function to an array. Despite being aware that t ...

Issues have been identified with React Native UI components RCTBubblingEventBlock and RCTDirectEventBlock not functioning as expected

In my custom native view within an Ignite project, I am facing a challenge with setting up communication from Objective-C to React Native. While the communication from React Native to iOS works using HTML injection, the reverse direction is not functioning ...

Implementing an extended interface as an argument in a function

Here is the code snippet for analysis: interface IUserData { FirstName: string, LastName: string, Email: string, Password: string } interface IState extends IUserData { isSuccess: boolean } const state: IState = { FirstName: &apo ...

Is there a way in JavaScript to format an array's output so that numbers are displayed with only two decimal places?

function calculateTipAmount(bill) { var tipPercent; if (bill < 50 ) { tipPercent = .20; } else if (bill >= 50 && bill < 200){ tipPercent = .15; } else { tipPercent = .10; } return tipPercent * bill; } var bills = ...

Uncovering design elements from Material UI components

The AppBar component applies certain styles to children of specific types, but only works on direct children. <AppBar title="first" iconElementRight={ <FlatButton label="first" /> }/> <AppBar title="second" iconElementRight={ <di ...

Executing multiple HTTP requests simultaneously in groups using an asynchronous for loop for each individual request

I'm currently working on running multiple requests simultaneously in batches to an API using an array of keywords. Read Denis Fatkhudinov's article for more insights. The issue I'm facing involves rerunning the request for each keyword with ...

Having trouble navigating to the bottom of a VUEJS app?

I've been working on developing a chatbot app that utilizes a REST API to stream content. While the functionality of the app is running smoothly, I have encountered an issue with the scroll to bottom feature. Instead of automatically scrolling to disp ...