6 votes

switchMap Le type void n'est pas assignable au type ObservableInput<{}>.

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.

1voto

Jan-Niklas Wortmann Points 291

Le problème n'est pas lié à la switchMap lui-même. C'est plutôt le fait que votre getLiveUrl est de type void. switchMap attend une fonction qui renvoie un observable comme paramètre d'entrée. Vous voulez donc getLiveUrl pour renvoyer un observable. Pour y parvenir, le code pourrait ressembler à ceci (notez que je ne suis pas sûr à 100 % de ce que signifie getBest est en train de faire, cela peut avoir un effet sur la solution) :

 public getLiveUrl(urls: any) {
        let promises = [];

        urls.forEach((url) => {
           promises.push(this.http.get(url + 'some/endpoint/')); 
        });

        return forkJoin(promises).pipe(
           map(data => this.getBest(data))
        );
    }

Je ne suis pas encore tout à fait sûr de ce que vous essayez de faire, mais j'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X