Utilizing Angular HttpClient and Observable to map foreign keys to data models

Imagine having a scenario where there are mothers and children entities, with the mother holding a foreign key linked to the child's ID. The challenge arises when the API can only retrieve all mothers and children from separate endpoints, resulting in a complete list of mothers and potential children. The question now becomes how to resolve this foreign key on the application side, preferably using a transformer.

There is uncertainty about whether handling it this way is the most efficient approach. A preferred solution would involve avoiding child: Observable<Child> within the Mother class, favoring child: Child instead.

models.ts

/**
 * Representing entities fetched from the API
 */
export class ApiMother {
  id: number;
  child: number;
}

export class ApiChild {
  id: number;
  firstName: string;
  lastName: string;
}

/**
 * Entities used in the application
 */
export class Mother {
  id: number;
  child: Observable<Child>
}

export class Child {
  id: number;
  name: string;
}

mother.service.ts

export class MotherService extends BaseApiService {

  constructor(
    private http: HttpClient,
    private transformer: MotherTransformerService,
  ) {
    super(auth);
  }

  /**
   *  Retrieve a list of mothers.
   *
   * @return An `Observable` of `Mother` representing the request, with a JSON response body.
   */
  public getAll(): Observable<Mother[]> {
    return this.http
      .get<ApiMother[]>(this.url)
      .pipe(map(
        obj => this.transformer.fromApiModel(obj))
      );
  }
}

mother-transformer.service.ts

export class MotherTransformerService {

  constructor(
    private childService: ChildService
  ) {
  }

  /**
   * Adapt the `Mother` object to meet application requirements.
   *
   * @param apiModel: ApiMother
   * @return Mother
   */
  fromApiModel(apiModel: ApiMother): Mother {
    let model = new Mother;

    const child: Observable<Child> = this.childService.findOne(apiModel.child);

    model = {
      id: apiModel.id,
      child,
    };

    return model;
  }
}

child.service.ts

export class childService extends BaseApiService {

  constructor(
    private http: HttpClient,
    private transformer: ChildTransformService, // Combines `lastName` and `firstName` into `name`.
  ) {
    super(auth);
  }

  /**
   * Obtain a list of children.
   *
   * @return An `Observable` of `Child[]` for the request, with a JSON response body.
   */
  public getAll(): Observable<Child[]> {
    return this.http
      .get<ApiChild[]>(this.url, this.options)
      .pipe(map(
        list => list.map(obj => this.transformer.fromApiModel(obj)))
      );
  }

  /**
   * Retrieve a single `Child` based on the specified ID.
   *
   * @param id: ID of the `Child`
   * @return An `Observable` of `Child`
   */
  public findOne(id: number): Observable<Child> {
    return this.getAll()
      .pipe(
        filter((child: Child) => child.id === id)[0]
      );
  }
}

I appreciate any advice or suggestions offered. It's possible that this topic overlaps with another thread, but I couldn't locate a related query.

Answer №1

To improve the efficiency of retrieving child data, consider fetching all children in one go and storing them for future access. Alternatively, implement a method to fetch a single child on demand if needed.

Updating the mother transform to return an observable would be beneficial in this scenario.

export class MotherTransformerService {

  constructor(
    private childService: ChildService
  ) {
  }

  /**
   * Mutate `Mother` to fit application needs.
   *
   * @param apiModel: ApiMother
   * @return Mother
   */
  fromApiModel(apiModel: ApiMother): Observable<Mother> {
    let model = new Mother;

    const child: Observable<Child> = this.childService.findOne(apiModel.child);

    
    return child.pipe(map(c => {
       model = {
         id: apiModel.id,
         child: c,
        };

        return model;

    });
  }
}

By converting fromApiModel for mother to an Observable, switchMap can now be used instead of map.

export class MotherService extends BaseApiService {

  constructor(
    private http: HttpClient,
    private transformer: MotherTransformerService,
  ) {
    super(auth);
  }

  /**
   *  Return list of mothers.
   *
   * @return An `Observable` of `Mother` for the request, with a response body in JSON.
   */
  public getAll(): Observable<Mother[]> {
    return this.http
      .get<ApiMother[]>(this.url)
      .pipe(switchMap(
        obj => this.transformer.fromApiModel(obj))
      );
  }
}

Now we have Mother.child: Child instead of

Mother.child: Observable<Child>
.

Note that there may be some import or syntax issues to resolve based on your current code implementation.

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

Compiling the configureStore method with the rootreducer results in compilation failure

Software Setup @angular/cli version: ^9.1.2 System Details NodeJS Version: 14.15.1 Typescript Version: 4.0.3 Angular Version: 10.1.6 @angular-redux/store version: ^11.0.0 @angular/cli version (if applicable): 10.1.5 OS: Windows 10 Expected Outcome: ...

The React 18 update in StrictMode is causing a blank screen to appear

After upgrading my react application to version 18.2.0, I followed the documentation and started the application using react-dom/client. import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; ...

Troubleshooting: Empty Rows displayed in PrimeNG Table

Experimenting with Angular 8 and the primeNG package, I'm facing an issue where I cannot retrieve values. Despite using the {{staff[col.field]}} syntax, I only get empty rows of data. However, when I utilize the interface definition like {{staff.Emplo ...

Deep copying with Object.assign can lead to unexpected issues

I am currently working with an object array that needs to be transformed before it is sent to the controller. Here is the Angular code snippet I am using: sourceObjArray: SourceObject[] = [..]; targetObjArray: SourceObject[]= []; targetObjArray = object. ...

Restoring scroll position in Next.js when the page is reloaded

Problem Description I am facing an issue with the sticky header functionality I have implemented. It relies on a useEffect hook to monitor its scroll Y offset state. However, when I reload the page, it fails to detect the position until I manually scroll ...

Troubleshooting: Why is .detectChanges() not functioning as expected

My current task involves writing tests for a chat application built with Angular. I am focusing on testing the following Angular template code snippet: <div class="title-menu-container" fxLayoutAlign="center center"> <button id="save-title-butt ...

The error message indicates that the 'aboutData' property is not found within the 'never[]' data type

What is the correct method for printing array elements without encountering the error message "Property 'post_title' does not exist on type 'never[]'?" How can interfaces be used to define variables and utilize them in code for both ab ...

Tips for retrieving the most recent number dynamically in a separate component without needing to refresh the page

Utilizing both the Helloworld and New components, we aim to store a value in localStorage using the former and display it using the latter. Despite attempts to retrieve this data via computed properties, the need for manual refreshing persists. To explore ...

Setting up the view for 2-factor authentication in Umbraco 10: A guide for Angular or C# users

In my efforts to customize the two-factor authentication view for users with 2FA enabled in Umbraco, I've created a provider called UmbracoUserAppAuthenticator and used builder.Services.Configure to add the 'SetupViewPath', which is function ...

Updating the button text in Angular 7

Here's a question: <button (click)="activateMotion(1)"> <img class="emotion-icon" id="positive-icon" src="" /> </button> <button (click)="activateMotion(-1)"> <img class="emotion-icon" id="negative-icon" src="" /&g ...

Expanding rows in Angular 10 causes fluctuations in the width of Bootstrap tables?

My table has expandable rows when clicked. Here is the current setup: <table class="table table-striped" > <thead> <tr> <th scope="col">a</th> <th scope="col">b</th> ...

When utilizing Monggose, Angular, and Node, a route containing the deleteOne method repeatedly reports that the object has been successfully deleted, despite the delete count remaining

I've encountered a similar issue to others, but their solutions didn't work for me. I'm working on a small MEAN app with mongoose and facing a problem when trying to delete a user or any other object stored in the collection. The route seems ...

The type 'void | Observable<User>' does not include the property 'subscribe'. Error code: ts(2339)

authenticate() { this.auth.authenticate(this.username, this.password).subscribe((_: any) => { this.router.navigateByUrl('dashboard', {replaceUrl: true}); }); } I'm puzzled by this error message, I've tried a few solu ...

What are the best ways to implement advanced data filtering in JavaScript?

Is there a way to improve the filtering of data in the ngx-datatable? Currently, I have a filter that only works for the "Name" column. How can I adjust it to filter data from other columns like "id" or "phone" as well? filt ...

Passing NextJS props as undefined can lead to unexpected behavior and

Struggling with dynamically passing props to output different photo galleries on various pages. One of the three props works fine, while the others are undefined and trigger a warning about an array with more than one element being passed to a title elemen ...

Using JSON as a variable solely for determining its type and guaranteeing that the import is eliminated during compilation

In my TypeScript backend project with Node as the target runtime, I have a JSON file that is auto-generated within my repository. I use the following code to import the JSON file in order to get the type of the JSON object: import countries from '../g ...

What is the process for updating the authService in Angular to return an observable using the map function from the previous version?

I recently followed a tutorial on JWT authentication with ASP.NET Core 2 Web API, Angular 5, .NET Core Identity, and Facebook login. The tutorial can be found here. While the tutorial was well-written, it utilized an older version of Angular (I am using An ...

Show a blank space if there is no data returned from the backend

My scenario involves using an HTTP service to retrieve data from the backend for display in a Material field on the frontend. controller.ts import { Job } from '../models/job.model'; //this is my interface model export class JobComponent { ...

Executing npm and ng commands via an Ant script on a Windows machine leads to the error message "The specified file could not be found."

When attempting to execute the following Ant script, which runs the "npm" command: <target name ="test"> <exec executable="npm" failonerror="true"> <arg value="install" /> </exec> </target> An error occurs, i ...

A guide on incorporating Typescript into Material UI v5 themes

A similar question has been asked previously, however... I am looking to enhance my color options by adding variants such as success, warning, and more choices within the background category (palette.background). Specifically interested in a lite option t ...