web-dev-qa-db-fra.com

Comment générer une erreur de l'opérateur de carte RxJS (angulaire)

Je veux jeter une erreur de l'opérateur de mon observable map en fonction d'une condition. Par exemple, si les données d'API correctes ne sont pas reçues. S'il vous plaît voir le code suivant:

private userAuthenticate( email: string, password: string ) {
    return this.httpPost(`${this.baseApiUrl}/auth?format=json&provider=login`, {userName: email, password: password})
        .map( res => { 
            if ( res.bearerToken ) {
                return this.saveJwt(res.bearerToken); 
            } else {
                // THIS DOESN'T THROW ERROR --------------------
                return Observable.throw('Valid token not returned');
            }
        })
        .catch( err => Observable.throw(this.logError(err) )
        .finally( () => console.log("Authentication done.") );
}

Fondamentalement, comme vous pouvez le voir dans le code, si la réponse (objet res) n'a pas 'bearerToken', je souhaite supprimer une erreur. Ainsi, dans mon abonnement, il entre dans le deuxième paramètre (handleError) mentionné ci-dessous.

.subscribe(success, handleError)

Aucune suggestion?

54
Hassan

Jetez simplement l'erreur à l'intérieur de l'opérateur map(). Tous les rappels dans RxJS sont encapsulés dans des blocs try-catch. Ils seront donc capturés puis envoyés sous forme de notification error.

Cela signifie que vous ne renvoyez rien et lancez simplement l'erreur:

map(res => { 
  if (res.bearerToken) {
    return this.saveJwt(res.bearerToken); 
  } else {
    throw new Error('Valid token not returned');
  }
})

La throwError() (ancienne Observable.throw() dans RxJS 5) est un observable qui envoie simplement une notification error mais _ map() ne se soucie pas de ce que vous retournez. Même si vous retournez un observable de map(), il sera transmis sous la forme de notification next.

Dernière chose, vous n'avez probablement pas besoin d'utiliser .catchError() (ancien catch() dans RxJS 5). Si vous devez utiliser des effets secondaires en cas d'erreur, utilisez plutôt tap(null, err => console.log(err)) (ancien do() dans RxJS 5) par exemple.

Jan 2019: Mise à jour pour RxJS 6

94
martin

Si vous sentez que throw new Error() semble inobservable, vous pouvez utiliser switchMap:

// RxJS 6+ syntax
this.httpPost.pipe(switchMap(res => { 
   if (res.bearerToken) {
      return of(this.saveJwt(res.bearerToken)); 
   } 
   else {
      return throwError('Valid token not returned');
   }
});

ou plus précisément:

this.httpPost.pipe(switchMap(res => (res.bearerToken) ? 
                                    of(this.saveJwt(res.bearerToken)) : 
                                    throwError('Valid token not returned')
));

Le comportement sera le même, c'est juste une syntaxe différente.

Vous dites littéralement "passer" de l'observable http dans le canal à un observable différent, qui consiste simplement à "envelopper" la valeur de sortie ou à une nouvelle "erreur".

N'oubliez pas de mettre of sinon vous obtiendrez des messages d'erreur déroutants.

En outre, l'intérêt de 'switchMap' réside dans le fait que vous pouvez renvoyer une nouvelle 'chaîne' de commandes si vous le souhaitez - quelle que soit la logique à utiliser avec saveJwt.

6
Simon_Weaver

Même si cette question a déjà trouvé une réponse, j'aimerais partager ma propre approche (même si elle n'est que légèrement différente de la précédente).

Je déciderais ce qui est renvoyé séparément du mappage et vice versa. Je ne sais pas quel opérateur est le meilleur pour cela, donc je vais utiliser tap.

this.httpPost.pipe(
  tap(res => { 
    if (!res.bearerToken) {
      throw new Error('Valid token not returned');
    }
  }),
  map(res => this.saveJwt(res.bearerToken)),
);
0
christo8989