#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); у них другой тип