Trying to handle an OAuth login scenario where if the user lands on a page with authorization_code
in the query string, we process the token and continue
I guess that's by design.
queryParams
is the BehaviorSubject
As you can see in the docs
One of the variants of Subjects is the BehaviorSubject, which has a notion of "the current value". It stores the latest value emitted to its consumers, and whenever a new Observer subscribes, it will immediately receive the "current value" from the BehaviorSubject.
As workaround you can use debounceTime
operator as follows:
import 'rxjs/add/operator/debounceTime';
this._route.queryParams
.debounceTime(200)
.subscribe(params => {
console.log(params);
});
You can wait until NavigationEnd event is done and then get the values or subscribe to changes:
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.route.queryParams.subscribe(params => {
console.log(params);
});
}
});
}
Router observables (as another answer mentions) are BehaviorSubject subjects, they differ from regular RxJS Subject
or Angular 2 EventEmitter
in that they have the initial value pushed to the sequence (an empty object in the case of queryParams
).
Generally the possibility of subscribing with initialization logic is desirable.
The initial value can be skipped with skip
operator.
this._route.queryParams
.skip(1)
.subscribe(params => ...);
But more natural way to handle this is to filter out all irrelevant params (initial params
falls into this category). Duplicate authorization_code
values can also be filtered with distinctUntilChanged
operator to avoid unnecessary calls to the backend.
this._route.queryParams
.filter(params => 'authorization_code' in params)
.map(params => params.authorization_code)
.distinctUntilChanged()
.subscribe(authCode => ...);
Notice that Angular 2 imports a limited amount of RxJS operators (at least map
in the case of @angular/router
). If full rxjs/Rx
bundle isn't used, it may be necessary to import extra operators (filter
, distinctUntilChanged
) that are in use with import 'rxjs/add/operator/<operator_name>'
.
Just use Location
class to get initial url, UrlSerializer
class to parse url, UrlTree
to get query params.
The best way to overcome this was subscribing router events, and processing query params only after the route is ticked to navigated
state:
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;
}
if you go to this link https://dev-hubs.github.io/ReactiveXHub/#/operators/conditional/skipUntil
1) Copy Paste this code in the code editor.
/* 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) Hit Run Button
You will see that you will alerted twice, one directly on subscription and second bcz of the last line.
The current result is not wrong, thats the philosophy behind Rx 'operators make things happen' you can lookup this decision tree to see the operator you are looking for http://reactivex.io/documentation/operators.html#tree I usually use skip(1)