OAuthログインシナリオを処理しようとして、ユーザーがクエリ文字列にauthorization_code
を含むページに到達した場合、トークンを処理して続行しますまたは彼らがそれなしでページに着地した場合、既存のトークンのローカルストレージをチェックし、それがまだ有効であることを確認し、その有効性に基づいてログインにリダイレクトするか続行します。
問題は、authorization_code
クエリ文字列パラメーターの存在を確認している場所で、サブスクリプションが2回起動されることです。初めて空になると、2回目は辞書に正しい値を持ちます。
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 (新しいウィンドウにポップアウトし、クエリ文字列?test=test
を追加する必要があります)。
質問
ルーターのオブザーバブル(別の回答の言及として) BehaviorSubject
件名 、通常のRxJS Subject
またはAngular 2 EventEmitter
は、シーケンスにプッシュされた初期値を持っているという点(queryParams
の場合は空のオブジェクト)。
一般に、初期化ロジックでサブスクライブする可能性が望まれます。
初期値は、skip
演算子を使用してスキップできます。
_this._route.queryParams
.skip(1)
.subscribe(params => ...);
_
しかし、これを処理するより自然な方法は、関係のないパラメーターをすべて除外することです(最初のparams
はこのカテゴリーに分類されます)。重複する_authorization_code
_値は、distinctUntilChanged
演算子でフィルタリングして、バックエンドへの不要な呼び出しを回避することもできます。
_this._route.queryParams
.filter(params => 'authorization_code' in params)
.map(params => params.authorization_code)
.distinctUntilChanged()
.subscribe(authCode => ...);
_
Angular 2は、限られた量のRxJS演算子(_@angular/router
_の場合は少なくともmap
)をインポートします。完全な_rxjs/Rx
_バンドルではない場合]使用される場合、_import 'rxjs/add/operator/<operator_name>'
_で使用されている追加の演算子(filter
、distinctUntilChanged
)をインポートする必要がある場合があります。
これを克服する最良の方法は、ルーターイベントをサブスクライブし、ルートが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;
}
これは設計によるものだと思います。
queryParams
はBehaviorSubject
docs でわかるように
サブジェクトのバリアントの1つにBehaviorSubjectがあり、これには「現在の値」という概念があります。コンシューマーに発行された最新の値を保存し、新しいオブザーバーがサブスクライブするたびに、すぐにBehaviorSubjectから「現在の値」を受け取ります。
回避策として、次のようにdebounceTime
演算子を使用できます。
import 'rxjs/add/operator/debounceTime';
this._route.queryParams
.debounceTime(200)
.subscribe(params => {
console.log(params);
});
NavigationEndイベントが完了するまで待機してから、値を取得するか、変更をサブスクライブできます。
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);
});
}
});
}
Location
クラスを使用して初期URLを取得し、UrlSerializer
クラスを使用してURLを解析し、UrlTree
を使用してクエリパラメータを取得します。
NgAfterViewInitメソッドにサブスクリプションコードを配置し、
ngAfterViewInit() {
this.route.queryParams.subscribe(params => {
debugger;
});
}