Разрешение внешних ключей для моделей данных с помощью Angular HttpClient и Observables - PullRequest
1 голос
/ 09 июля 2020

Предположим, у меня есть материнская и дочерняя сущности. У матери есть внешний ключ с идентификатором ребенка. Однако API может получить доступ ко всем матерям и детям только с разных конечных точек (а не с одной). В результате всегда получается полный список матерей и потенциальных детей. Теперь я хочу разрешить этот внешний ключ на стороне приложения. В лучшем случае с помощью трансформатора.

Я совершенно не уверен, правильный ли этот способ. Мое любимое решение - избегать использования child: Observable<Child> внутри class Mother. child: Child будет лучшим решением.

models.ts

/**
 * Entities coming from API
 */
export class ApiMother {
  id: number;
  child: number;
}

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

/**
 * Entities used in 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);
  }

  /**
   *  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(map(
        obj => this.transformer.fromApiModel(obj))
      );
  }
}

mother-transformer.service.ts

export class MotherTransformerService {

  constructor(
    private childService: ChildService
  ) {
  }

  /**
   * Mutate `Mother` to fit application needs.
   *
   * @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, // Concatinates `lastName` and `firstName` to `name`.
  ) {
    super(auth);
  }

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

  /**
   * Return one single `Child` correlating with specified ID.
   *
   * @param id: ID of `Child`
   * @return An `Observable` of `Child`
   */
  public findOne(id: number): Observable<Child> {
    return this.getAll()
      .pipe(
        filter((child: Child) => child.id === id)[0]
      );
  }
}

Я рад любому помощь или рекомендация. Скорее всего, эта ветка дублирует другую, но я не смог найти соответствующего вопроса.

1 Ответ

1 голос
/ 09 июля 2020

Ну, сначала я бы изменил то, как вы заводите ребенка. Прямо сейчас, каждый раз, когда вам нужен 1 ребенок, вы снова получаете полный список с сервера, а затем фильтруете его. Вы также можете просто получить их все в одном go и сохранить их как переменную в сервисе, чтобы к ним можно было получить доступ позже, или иметь подходящий метод для получения одного дочернего элемента с сервера по запросу, если данные могут становится устаревшим.

Вы можете решить это в будущем, но, сохраняя текущий код как есть, поможет изменение материнского преобразования для возврата наблюдаемого.

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;

    });
  }
}

Теперь, когда fromApiModel for mother является Observable, мы будем использовать switchMap вместо 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))
      );
  }
}

Теперь у нас есть Mother.child: Child вместо Mother.child: Observable<Child>.

Могут быть некоторые проблемы с импортом или небольшой синтаксис проблемы, поскольку это не в блиц-стеке для проверки, но, основываясь на вашем текущем коде, я думаю, вы, вероятно, могли бы понять это отсюда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...