Множественная подписка Angular 2

#angular #api #angular2-services

#angular #API #angular2-сервисы

Вопрос:

Здравствуйте, я разрабатываю приложение, которое будет развернуто на устройстве Android с подключенным устройством сканирования. Я разработал сервисную часть, которая получает отсканированные данные. Моя проблема сейчас в том, что мне приходится различать 3 вызова api, когда устройство начинает сканирование. Я объясню себя лучше:

       scannedResult$ = new BehaviorSubject<Api1Response>(null);
      scannedResultApi2$ = new BehaviorSubject<Api2Response>(null);
      scannedResultApi3$ = new BehaviorSubject<Api2Response>(null);

      constructor(
        private scanData: ScanDataService,
        private api: ApiService,
        private scannedDestination: DestinationService
      ) {
        super();
        const scannedResult$ = this.scanData.scanResult$
        .pipe(
          filter(_ => this.scannedDestination.scanDestination === 'Api1'),
          tap(_ => (this.scanning = false)),
          switchMap(scanned=>
            this.api.callApi(Api1Query, {
              DataBar: scanned.DataBar
            })
          )
        )
      .pipe(
      filter(_ => this.scannedDestination.scanDestination === 'Api2'),
      tap(_ => (this.scanning = false)),
      switchMap(scanned =>
        this.api.callApi(Api2Command, {
          DataBar: scanned.DataBar
        })
      )
    )
    .pipe(
      filter(_ => this.scannedDestination.scanDestination === 'Api3'),
      tap(_ => (this.scanning = false)),
      switchMap(scanned =>
        this.api.callApi(Api3Command, {
          DataBar: scanned.DataBar
        })
      )
    )
    .subscribe(result => {
      this.scannedResult$.next(result);
    });
}
  

Внутри DestinationService я определил это:

 scannedDestination: 'Api1' | 'Api2' | 'Api3';
  

И на основе этого scannedDestination я применяю фильтр для выполнения разных вызовов.
Моя проблема в том, что это решение не работает? Это потому, что мне нужно установить три разные подписки?
Как я могу заставить это работать?

Спасибо

Ответ №1:

Вы можете использовать switch/case in switchMap , если хотите сделать это за 1 подписку:

 const scannedResult$ = this.scanData.scanResult$
        .pipe(
          tap(_ => (this.scanning = false)),
          switchMap(scanned=> {
            let apiCallType = '';
            switch(this.scannedDestination.scanDestination){
              case 'Api1':{
               apiCallType = Api1Query;
               break;
              },
              case 'Api2':{
               apiCallType = Api2Command;
               break;
              },
              case 'Api3':{
               apiCallType = Api3Command;
               break;
              }
            }
            return combineLatest(
                     this.api.callApi(apiCallType, {
                       DataBar: scanned.DataBar
                     }),
                     of(this.scannedDestination.scanDestination)
                   )
          })
        )
.subscribe(([result, apiType]) => {

       switch(apiType){
              case 'Api1':{
               this.scannedResult$.next(result);
               break;
              },
              case 'Api2':{
               this.scannedResultApi2$.next(result);
               break;
              },
              case 'Api3':{
               this.scannedResultApi3$.next(result);
               break;
              }
            }

});
  

Редактировать Примечание: Я добавил combineLatest передавать apiCallType значение с самой подпиской, чтобы выбрать, какое BehaviorSubject вызывать, когда подписка генерирует значение. Следовательно, к switch/case обратному вызову добавляется второй. subscribe

combineLatest должен быть импортирован следующим образом: import { combineLatest } from 'rxjs';

Обновление 2

Как вы спросили в комментариях, если вы хотите вызвать другой API (я предполагаю, что это независимый вызов API, означающий, что он не имеет ничего общего с ответом), вы можете использовать tap из rxjs

        const scannedResult$ = this.scanData.scanResult$
        .pipe(
          tap(_ => (this.scanning = false)),
          switchMap(scanned=> {
            let apiCallType = '';

            let is2or3 = false; // flag to check if Api2Command or Api3Command

            switch(this.scannedDestination.scanDestination){
              case 'Api1':{
               apiCallType = Api1Query;
               break;
              },
              case 'Api2':{
               apiCallType = Api2Command;
               is2or3 = true;
               break;
              },
              case 'Api3':{
               apiCallType = Api3Command;
               is2or3 = true;
               break;
              }
            }


             let apiToCall = this.api.callApi(apiCallType, {
                       DataBar: scanned.DataBar
                     });

            if(is2or3){
                apiToCall = apiToCall.pipe(
                    tap(() => {
                         // do the other API call here.
                         // this.anotherApiCall();
                    })
                )
            }

            return combineLatest(
                     apiToCall,
                     of(this.scannedDestination.scanDestination)
                   )
          })
        )
       .subscribe(([result, apiType]) => {

           // same as above

        });
  

выполните / коснитесь:https://www.learnrxjs.io/operators/utility/do.html

Комментарии:

1. здравствуйте, спасибо за ответ, но у них другой тип: scannedResult $ = новый объект BehaviorSubject<Api1Response>(null); scannedResultApi2 $ = новый объект BehaviorSubject<Api2Response>(null); scannedResultApi3 $ = новый объект BehaviorSubject<Api2Response>(null); Правильно ли подписываться таким образом?

2. Если это отдельные и разные типы объектов, я думаю, что лучше создать 3 разные подписки с 3 разными каналами фильтрации.

3. не могли бы вы, пожалуйста, рассказать мне, как это сделать?

4. Когда я обновлял ответ, я увидел, что неправильно понял реальную проблему. Итак, я улучшил решение. Надеюсь, это поможет.

5. Мне это нравится, я попробовал, но, к сожалению, я получаю «Аргумент типа ‘any[]’ не может быть присвоен параметру типа ‘(значение: [any, число]) => void’. Тип ‘any[]’ не обеспечивает соответствия подписи ‘(значение: [any, число]): void’.»

Ответ №2:

Вы можете попробовать это

  const scannedResult$ = this.scanData.scanResult$
        .pipe(
          filter(_ => ),
          tap(_ => (this.scanning = false)),
          switchMap(scanned=>
            this.api.callApi(this.scannedDestination.scanDestination === 'Api1'? Api1Query: (this.scannedDestination.scanDestination === 'Api2'? Api2Command : Api3Command), {
              DataBar: scanned.DataBar
            })
          ).subscribe(result => {
      this.scannedResult$.next(result);
    });
  

Комментарии:

1. здравствуйте, спасибо за ответ, но он не работает, потому что это: scannedResult $ = новый объект BehaviorSubject<Api1Response>(null); scannedResultApi2 $ = новый объект BehaviorSubject<Api2Response>(null); scannedResultApi3 $ = новый объект BehaviorSubject<Api2Response>(null); у них другой тип