Eugene Sukh
Eugene Sukh

Reputation: 2727

Create array of object from 3 arrays of objects

I have 3 private methods in the angular component which return arrays of objects.

I need to create one array of the object which will contain all 3. All of them has the same class

Here is it

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

Here is the code of 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 row this.timelineItems = [...classes, ...courses, ...requests]; I have this error

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

How I can fix this?

Upvotes: 1

Views: 180

Answers (2)

Owen Kelvin
Owen Kelvin

Reputation: 15083

The Problem

Consider the code below

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

The above code calls .get() method then calls .subscription() method. This indicates that this method actually returns a subscription and NOT an Observable. As the error indicates you are trying to iterate over these subscription hence the error

Solution

To solve this, there are various ways, my approach will be as below

  • get classes as Observable
  • get requests as Observable
  • get courses as Observable
  • combine these 3 Observables to one Observable
  • subscribe to the new Observable

See Below 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
    })
  }

See this solution on stackblitz

Upvotes: 2

Barremian
Barremian

Reputation: 31115

That isn't how asynchronous data works. Please refer here for more info on async data.

In short, you need to wait till the data is emitted by the source. In this specific case, you need to wait for the RxJS observables emit the values before trying to assign them. And seeing that you need to subscribe to multiple observables, you could use RxJS forkJoin function to trigger the requests in parallel

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');
      }
    });
  }
}

Please go through the link above. The variable this.timelineItems might still be empty when you try to access it outside the subscription as it might not yet be assigned the values.

In other words, the this.timelineItems would only be properly accessible inside the subscription.

Upvotes: 1

Related Questions