Essayer de gérer un scénario de connexion OAuth dans lequel, si l'utilisateur atterrit sur une page avec authorization_code
dans la chaîne de requête, nous traitons le jeton et continuons ou s'ils atterrissent sur la page sans cela, nous vérifions le stockage local pour leur jeton existant , assurez-vous qu’il est toujours valide et redirigez-le ou connectez-vous en fonction de sa validité.
Le problème est que, lorsque nous vérifions l'existence du paramètre chaîne de requête authorization_code
, l'abonnement est déclenché deux fois. La première fois, il est vide, la deuxième fois, il a la valeur correcte dans le dictionnaire.
app.component.ts
export class App implements OnInit {
constructor(private _router: ActivatedRoute) {
}
public ngOnInit(): void {
console.log('INIT');
this._route.queryParams.subscribe(params => {
console.log(params);
});
}
}
Plunker (vous devrez le faire apparaître dans une nouvelle fenêtre et ajouter une chaîne de requête ?test=test
).
Des questions
Les observables de routeur (comme une autre réponse le mentionne) sont des sujets BehaviorSubject
, ils diffèrent de RxJS Subject
ou Angular 2 EventEmitter
en ce qu'ils ont la valeur initiale poussée à la séquence (objet vide dans le cas de queryParams
).
Généralement, la possibilité de s'inscrire avec une logique d'initialisation est souhaitable.
La valeur initiale peut être ignorée avec l'opérateur skip
.
this._route.queryParams
.skip(1)
.subscribe(params => ...);
Mais une façon plus naturelle de gérer cela consiste à filtrer tous les paramètres non pertinents (la variable params
initiale entre dans cette catégorie). Les valeurs en double authorization_code
peuvent également être filtrées avec l'opérateur distinctUntilChanged
pour éviter les appels inutiles au serveur.
this._route.queryParams
.filter(params => 'authorization_code' in params)
.map(params => params.authorization_code)
.distinctUntilChanged()
.subscribe(authCode => ...);
Notez que Angular 2 importe un nombre limité d'opérateurs RxJS (au moins map
dans le cas de @angular/router
). Si le paquet complet rxjs/Rx
n'est pas utilisé, il peut être nécessaire d'importer des opérateurs supplémentaires (filter
, distinctUntilChanged
) utilisés avec import 'rxjs/add/operator/<operator_name>'
.
Le meilleur moyen de résoudre ce problème était de souscrire des événements de routeur et de traiter les paramètres de requête uniquement après que la route a été cochée sur l'état navigated
:
public doSomethingWithQueryParams(): Observable<any> {
let observer: Observer<any>;
const observable = new Observable(obs => observer = obs);
this.router.events.subscribe(evt => {
// this is an injected Router instance
if (this.router.navigated) {
Observable.from(this.activatedRoute.queryParams)
// some more processing here
.subscribe(json => {
observer.next(json);
observer.complete();
});
}
});
return observable;
}
Je suppose que c'est à dessein.
queryParams
est le BehaviorSubject
Comme vous pouvez le voir dans la docs
L'une des variantes de Subjects est BehaviorSubject, qui a un notion de "la valeur actuelle". Il stocke la dernière valeur émise à ses consommateurs, et chaque fois qu'un nouvel observateur souscrit, il le fera recevez immédiatement la "valeur actuelle" de BehaviorSubject.
En guise de solution de contournement, vous pouvez utiliser l'opérateur debounceTime
comme suit:
import 'rxjs/add/operator/debounceTime';
this._route.queryParams
.debounceTime(200)
.subscribe(params => {
console.log(params);
});
Utilisez simplement Location
class pour obtenir l'URL initiale, UrlSerializer
class pour analyser l'URL, UrlTree
pour obtenir les paramètres de requête.
si vous allez sur ce lien https://dev-hubs.github.io/ReactiveXHub/#/operators/conditional/skipUntil
1) Copier Coller _ ce code dans l'éditeur de code.
/* since queryParams is a BehaviorSubject */
var queryParams = new Rx.BehaviorSubject();//this will AUTOMATICALLY alert 'undefined'
var subscription = queryParams.subscribe(
function (x) {
alert(x);
},
function (err) {
alert(err);
},
function () {
alert('Completed');
});
queryParams.onNext('yay');//this will cause to alert 'yay'
2) Appuyez sur le bouton Run
Vous verrez que vous serez alerté deux fois, un directement sur abonnement et un deuxième bcz de la dernière ligne.
Le résultat actuel n’est pas faux, c’est la philosophie qui sous-tend les opérateurs de Rx: «Faites bouger les choses», vous pouvez consulter cet arbre décisionnel pour voir l’opérateur recherché http://reactivex.io/documentation/operators.html#tree J'ai l'habitude d'utiliser skip (1)
Vous pouvez attendre que l'événement NavigationEnd soit terminé, puis obtenir les valeurs ou vous abonner aux modifications:
constructor(private router: Router, private route: ActivatedRoute) { }
public ngOnInit(): void {
console.log('INIT');
this.router.events
.subscribe((event) => {
if (event instanceof NavigationEnd) {
// Get a good value
let initialParams = this.route.snapshot.queryParams;
console.log(initialParams);
// or subscribe for more changes
this.router.queryParams.subscribe(params => {
console.log(params);
});
}
});
}