Search code examples
javascriptangulartypescriptangular7angular8

Merge Results from Two GET Requests into Single Iterable Object in Angular 8/ Typescript with forkJoin


I'm using a forkJoin to get results from two separate endpoints. I can iterate on results[0] and results[1] however, I cannot go a level down to the values. IE: I cannot do results[0].amiiboId it always returns undefined, even though when I look at the full object of results[0] I can see it has an amiiboId (and all other fields).

I am assigning results[0] to a property called amiiboCollection but I need to use fields from both results[0] and results[1] in my template. Is there a way to merge these two objects so that my 2nd response is a child of the first response? Example below:

let amiibo = this.amiiboService.getAllAmiibo();
    let collection = this.amiiboService.getCollection(this.userId);

    forkJoin([amiibo, collection]).subscribe(results => {
      // results[0] is amiibo
      // results[1] is collection
      (results[0] as any).collection = results[1];
      this.amiiboCollection = results[0];


      console.log(this.amiiboCollection);
    });

The first end point will return:

[
{
    "amiiboId": 1,
    "series": {
        "seriesId": 1,
        "seriesName": "Super Smash Bros. series "
    },
    "name": "Mario",
    "imageUrl": "https://storage.googleapis.com/amiibo/9L3Onnk5QzEp9CY8FtCKu65GjBbwr05O.png",
    "naReleaseDate": "2014-11-21",
    "jpReleaseDate": null,
    "euReleaseDate": null,
    "auReleaseDate": null
},
{
    "amiiboId": 2,
    "series": {
        "seriesId": 1,
        "seriesName": "Super Smash Bros. series "
    },
    "name": "Donkey Kong ",
    "imageUrl": "https://storage.googleapis.com/amiibo/AO2t6rdJrmc6bn8ADwGC0v_IR1Y8Zju-.png",
    "naReleaseDate": "2014-11-21",
    "jpReleaseDate": null,
    "euReleaseDate": null,
    "auReleaseDate": null
}
]

And the second end point will return:

{
    "collectionId": 55,
    "amiiboId": 1,
    "userId": 12,
    "collected": "Y",
    "quantity": null,
    "collectedInBox": null,
    "collectedInBoxQuantity": null,
    "favorited": null,
    "wishlisted": null,
    "addDate": "2020-01-09T23:17:45.734+0000",
    "modDate": "2020-01-09T23:17:45.735+0000"
},
{
    "collectionId": 59,
    "amiiboId": 2,
    "userId": 12,
    "collected": "N",
    "quantity": null,
    "collectedInBox": null,
    "collectedInBoxQuantity": null,
    "favorited": null,
    "wishlisted": null,
    "addDate": "2020-01-11T19:09:36.177+0000",
    "modDate": "2020-01-11T19:09:36.177+0000"
},

I would like to have a combined object like this:

[
    {
    "amiiboId": 1,
    "series": {
        "seriesId": 1,
        "seriesName": "Super Smash Bros. series "
    },
    "name": "Mario",
    "imageUrl": "https://storage.googleapis.com/amiibo/9L3Onnk5QzEp9CY8FtCKu65GjBbwr05O.png",
    "naReleaseDate": "2014-11-21",
    "jpReleaseDate": null,
    "euReleaseDate": null,
    "auReleaseDate": null,
    "collection": {
        "collectionId": 55,
    "amiiboId": 1,
    "userId": 12,
    "collected": "Y",
    "quantity": null,
    "collectedInBox": null,
    "collectedInBoxQuantity": null,
    "favorited": null,
    "wishlisted": null,
    "addDate": "2020-01-09T23:17:45.734+0000",
    "modDate": "2020-01-09T23:17:45.735+0000"
    },
    },
    ]

I am also open to other ways to do this, or if there is a "right way" to handle this, I would love to learn more about that.

Any help is greatly appreciated!

Thanks, Travis W.


Solution

  • I found a way to handle what I was looking for: (Important note, I was being thrown off by Typescript type-mismatch errors, but they were not preventing compile).

      ngOnInit() {
    
        let amiiboRequest = this.amiiboService.getAllAmiibo();
        let collectionRequest = this.amiiboService.getCollection(this.userId);
    
        forkJoin([amiiboRequest, collectionRequest]).subscribe(results => {
          const amiiboResponse = results[0];
          const collectionResponse = results[1];
    
          this.amiiboCollection = [];
    
          let collected;
          amiiboResponse.forEach((amiibo) => {
            collectionResponse.forEach((collection) => {
              if (amiibo.amiiboId == collection.amiiboId) {
                collected = true;
                this.amiiboCollection.push({...amiibo, ...collection, collected});
              }
            });
    
            if (!collected) {
              this.amiiboCollection.push({...amiibo, collected});
            }
    
            collected = false;
          });
    
          console.log(this.amiiboCollection);
        });
      }
    

    Definitely a cleaner way, I will update this at a later time with that once I figure it out.

    UPDATE: The "cleaner way" for me, was changing the backend to handle a lot of the heavy lifting. I set my model up to include Collection (as an Object) which in this case resulted in only needing the following on the front end:

    amiiboCollection = [];
    
    ngOnInit() {
        this.amiiboService.getAllAmiibo()
        .subscribe( results => {
          this.amiiboCollection = results;
        });
      }
    

    This isn't always going to be the answer, but in this case I felt it worked the best. The original solution is still above for anyone who doesn't have the option I had to make changes to their backend.