Trying to figure out how to switchMap an Organization to a Base in an Angular Component

46 Views Asked by At

So I have an API that I am trying to consume data from. The data being retuned:

Org

baseId: 1
createdAt: "2018-11-14T04:35:35.345Z"
id: 1
isActive: true
orgName: "Test Organization 1"
orgOwner: 2
subscriptionCode: "testorg1"
updatedAt: "2018-11-14T04:35:35.34

Base

bandwidthApiSecret: "xxxxxx"
bandwidthApiToken: "t-xxxxxx"
bandwidthUserId: "u-xxxxxx"
baseName: "Test AFB 1"
basePhoneNumber: "+18442367381"
createdAt: "2018-11-14T04:35:35.123Z"
id: 1
isActive: true
updatedAt: "2018-11-14T04:35:35.123Z"

What I would like to do, but fail to comprehend is using rxjs operations to insert the organization where needed. So if a base has one or more organizations it should be inserted into the bases. Here are my interfaces:

Base Interface

import { Organization } from './organization';
import { BaseManager } from './base-manager';

export interface Base {
    id: number;
    basePhoneNumber: string;
    baseName: string;
    bandwidthUserId: string;
    bandwidthApiToken: string;
    bandwidthApiSecret: string;
    createdAt?: Date;
    updatedAt?: Date;
    orgs?: Organization;
    managers?: BaseManager;
}

Org Interface

export interface Organization {
    id: number;
    orgName: string;
    orgOwner: string;
    baseId: number;
    subscriptionCode: string;
    isActive: boolean;
    createdAt: Date;
    updatedAt: Date;
}

I found this JSBin that sort of looks like it's doing what I need to accomplish. Due to my lack of comprehension of the subject I am failing to make it work. Here's a snippet of my Angular Component:

import { Component, OnInit } from '@angular/core';
import { Base } from 'src/app/core/interfaces/base';
import { BaseService } from 'src/app/core/services/base.service';
import { OrgService } from 'src/app/core/services/org.service';
import { Organization } from 'src/app/core/interfaces/organization';
import { switchMap } from 'rxjs/operators';

@Component({
  selector: 'app-base-list',
  templateUrl: './base-list.component.html',
  styleUrls: ['./base-list.component.css']
})
export class BaseListComponent implements OnInit {

  bases: Base[];
  orgs: Organization[];

  constructor(private baseService: BaseService, private orgService: OrgService) { }

  ngOnInit() {
    this.loadOrgs();
    this.loadBases();
  }

  loadBases() {
    this.baseService.getAllBases()
      .subscribe(
        res => {
          this.bases = res['bases'];
          console.log(this.bases);
        }
      );
  }
  // loadBases(): Base[] {
  //   this.baseService.getAllBases()
  //     .subscribe(
  //       res => {
  //         this.bases = res['bases'].pipe(
  //           switchMap( base => {
  //             return this.getOrg(base.id).map(
  //               base => {
  //                 return Object.assign(base, {base, {org: this.orgs});
  //               }
  //             )
  //           })
  //         );
  //       });
  // }

  loadOrgs() {
    this.orgService.getOrgs()
      .subscribe(
        res => {
          this.orgs = res['orgs'];
          console.log(this.orgs);
        }
      );
  }

  getOrg(baseId) {
    this.bases.filter(base => base.id === baseId);
  }

}

Any help on the matter would be greatly appreciated.

Cheers,

Coach

1

There are 1 best solutions below

1
On

There are three issues: First, res['bases'] is not an Observable and therefore can't be piped. This function invocation is not possible:

res['bases'].pipe(...)

Second, you are trying to assign the Observable to the property this.bases which has the type Base[] and not Observable<Base[]>. The assignment would therefore not be valid.

Third, even if res['bases'] were an Observable, you are never subscribing to it, so it won't ever emit any notifications.

The solution is one the one hand to not subscribe before the switchMap, but afterwards and on the other hand to correctly handle the array collection, e.g. with zip. This is how it would look like:

this.baseService.getAllBases().pipe(
        switchMap(bases =>
            zip(...bases.map(base =>
                    this.getOrg(base.id).pipe(
                        map(base => Object.assign(base, {orgs: this.orgs}))
                    )
                )
            )
        )
    ).subscribe(bases =>
        this.bases = bases
    )

Now, another issue is that you aren't waiting for loadOrgs to compete before trying to load the bases. This should also be concatenated like this:

loadOrgs(): Observable<Organization[]> {
    return this.orgService.getOrgs().pipe(
        tap(
            res => {
                this.orgs = res['orgs'];
                console.log(this.orgs);
            }
        )
    )
}

And in ngOnInit:

ngOnInit() {
    this.loadOrgs().subscribe(() => {
        this.loadBases();
    });
}

As a rule of thumb, be sure to always subscribe to your Observables, otherwise you won't actually execute anything. There are some pipes that do subscribe to inner Observables, like switchMap, but there must actually be a Subscriber for the switchMap pipe as well in order for it to take effect.