J'utilise Angular http et RXJS pour récupérer certains paramètres de l'application. Le http utilise RXJS shareReplay pour éviter de multiplier les mêmes appels.
Cela fonctionne comme prévu, et en inspectant l'onglet Réseaux, je ne vois qu'un seul appel à mon API dorsale.
Certains composants de mon application appelleront le service et utiliseront le résultat pour rendre la page. D'autres composants doivent appeler le service comme ci-dessus, mais ensuite faire "quelque chose" avec le résultat, par exemple d'autres appels http, puis rendre la page. Lorsque j'essaie de faire ce dernier, j'obtiens une erreur de type :
Argument of type '(result: any) => void' is not assignable to parameter of type '(value: any, index: number)' => ObservableInput<{}>
Sur l'utilisation de mon switchMap.
-- Exemple simple de travail sans switchMap ----
cart.component.ts
ngOnInit() {
this.systemPreferences.getPreferences()
.subscribe(
(result: any) => {
this.headerTitle = result.title || '';
},
(err) => {
console.log('there has been an error');
},
() => // Some completion code
);
}
systemPreferences.service.ts :
const CACHE_SIZE = 1;
export class SystemPreferences {
private $cache: Observable<Object>
private requestConfig() {
return this.http.get("/api/some/endpoint").pipe(
map(response => response.value)
);
}
public getPreferences() {
if (!this.cache$) {
this.cache$ = this.requestConfig().pipe(
shareReplay(CACHE_SIZE)
);
}
return this.cache$;
}
}
/api/some/endpoint exemple de réponse :
{
"appName": "Tables Forks and Spoons",
"title": "My title",
"regionUrls": [
"http://www.region1.com:8021",
"http://www.region2.com:8021",
"http://www.region3.com:8021",
"http://www.region4.com:8021"
],
"nonRegionUrls": [
"http://www.non-region1.com:8021",
"http://www.non-region2.com:8021",
"http://www.non-region3.com:8021",
"http://www.non-region4.com:8021"
]
}
-- Exemple simple d'erreur de type avec switchMap ----
cart.component.ts
ngOnInit() {
this.systemPreferences.getPreferences()
.pipe(
.switchMap((result: any) => {
this.systemPreferences.getLiveUrl(result['regionUrls']);
}))
.subscribe(
(result: any) => {
this.liveLink = result.liveLink; // result.livelink is added as part of this.systemPreferences.getLiveUrl above
this.headerTitle = result.title || '';
},
(err) => {
console.log('there has been an error');
}
);
}
systemPreferences.service.ts :
const CACHE_SIZE = 1;
export class SystemPreferences {
private $cache: Observable<Object>
private requestConfig() {
return this.http.get("/api/some/endpoint").pipe(
map(response => response.value)
);
}
public getPreferences() {
if (!this.cache$) {
this.cache$ = this.requestConfig().pipe(
shareReplay(CACHE_SIZE)
);
}
return this.cache$;
}
public getLiveUrl(urls: any) {
let promises = [];
urls.forEach((url) => {
promises.push(this.http.get(url + 'some/endpoint/'));
});
const results = forkJoin(promises) {
.subscribe((data: any) => {
return this.getBest(data);
}
}
}
public getBest(data: any) {
// Loops through data, and returns a single URL as a string
// 'http//www.thebesturltouse.com'
}
}
Mise à jour :
En essayant de renvoyer un second observable comme suggéré, voir ci-dessous, on obtient la même erreur de type void que ci-dessus.
cart.component.ts
ngOnInit() {
this.systemPreferences.getPreferences()
.pipe(
.switchMap((result: any) => {
return this.systemPreferences.getLiveUrl(result['regionUrls']);
}))
.subscribe(
(result: any) => {
this.liveLink = result.liveLink; // result.livelink is added as part of this.systemPreferences.getLiveUrl above
this.headerTitle = result.title || '';
},
(err) => {
console.log('there has been an error');
}
);
}
systemPreferences.service.ts :
const CACHE_SIZE = 1;
export class SystemPreferences {
private $cache: Observable<Object>
private $switchObs: Observable<Object>
private requestConfig() {
return this.http.get("/api/some/endpoint").pipe(
map(response => response.value)
);
}
public getPreferences() {
if (!this.cache$) {
this.cache$ = this.requestConfig().pipe(
shareReplay(CACHE_SIZE)
);
}
return this.cache$;
}
public getLiveUrl(urls: any) {
let promises = [];
urls.forEach((url) => {
promises.push(this.http.get(url + 'some/endpoint/'));
});
const results = forkJoin(promises) {
.subscribe((data: any) => {
this.$switchObs = this.getBest(data);
return this.$switchObs;
}
}
}
public getBest(data: any) {
// Loops through data, and returns a single URL as a string
// 'http//www.thebesturltouse.com'
}
}
Mise à jour de plusieurs cartes de commutation
cart.component.ts
ngOnInit() {
this.systemPreferences.getPreferences()
.pipe(
.switchMap((result: any) => {
return this.systemPreferences.getLiveUrl(result['regionUrls']);
}),
.switchMap((liveRegion: any) => {
return this.systemPreferences.getBest(liveRegion);
}))
.subscribe(
(bestUrl: String) => {
console.log('best');
console.log(bestUrl);
},
(err) => {
console.log('there has been an error');
}
);
}
systemPreferences.service.ts :
const CACHE_SIZE = 1;
export class SystemPreferences {
private $cache: Observable<Object>
private $switchObs: Observable<String>
private requestConfig() {
return this.http.get("/api/some/endpoint").pipe(
map(response => response.value)
);
}
public getPreferences() {
if (!this.cache$) {
this.cache$ = this.requestConfig().pipe(
shareReplay(CACHE_SIZE)
);
}
return this.cache$;
}
public getLiveUrl(urls: any) {
let promises = [];
urls.forEach((url) => {
promises.push(this.http.get(url + 'some/endpoint/'));
});
return forkJoin(promises);
}
public getBest(data: any) {
// Loops through data, and returns a single URL as a string
//
return this.$switchObs; // = 'http//www.thebesturltouse.com'
}
}
Avec ce qui précède, sur console.log('best')/console.log(bestUrl), je vois :
'best'
h
'best'
t
'best'
t
'best'
p
'best'
:
'best'
/
'best'
/
'best'
w
'best'
w
'best'
w
'best'
.
'best'
b
etc etc etc
Plutôt que la chaîne http//www.thebesturltouse.com
Mise à jour.
Dans l'exemple multi switchMap, si je retourne Obserbable.of(this.$switchObs) ; j'obtiens l'URL simple attendue.
Cela ne me semble pas être la meilleure façon de résoudre le problème.