尝试处理OAuth登录方案,如果用户登陆查询字符串中包含authorization_code
的网页,我们会处理该标记并继续或,如果他们落在页面上我们检查本地存储的现有令牌,确保它仍然有效,并根据其有效性重定向登录或继续。
问题是,在我们检查authorization_code
查询字符串参数是否存在的情况下,订阅会触发两次。第一次是空的,第二次在字典中有正确的值。
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
)。
问题
答案 0 :(得分:18)
路由器observables(作为另一个答案提及)are BehaviorSubject
subjects,它们与常规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运算符(在map
的情况下至少@angular/router
)。如果未使用完整的rxjs/Rx
捆绑包,则可能需要导入与filter
一起使用的额外运算符(distinctUntilChanged
,import 'rxjs/add/operator/<operator_name>'
)。
答案 1 :(得分:6)
解决此问题的最佳方法是订阅路由器事件,并仅在路由勾选到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;
}
答案 2 :(得分:3)
我猜这是设计的。
queryParams
是 BehaviorSubject
正如您在docs
中所看到的主题的变体之一是BehaviorSubject,它有一个 “当前价值”的概念。它存储发出的最新值 它的消费者,每当新的Observer订阅时,它都会 立即从BehaviorSubject接收“当前值”。
作为解决方法,您可以使用debounceTime
运算符,如下所示:
import 'rxjs/add/operator/debounceTime';
this._route.queryParams
.debounceTime(200)
.subscribe(params => {
console.log(params);
});
答案 3 :(得分:1)
您可以等到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);
});
}
});
}
答案 4 :(得分:0)
只需使用Location
类获取初始网址,UrlSerializer
类来解析网址,UrlTree
以获取查询参数。
答案 5 :(得分:0)
如果您转到此链接https://dev-hubs.github.io/ReactiveXHub/#/operators/conditional/skipUntil
1)在代码编辑器中复制粘贴此代码。
/* 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)点击运行按钮
你会看到你会提醒两次,一次是直接订阅,另一次是最后一行的bcz。
目前的结果并没有错,那就是Rx'运营商背后的理念让事情发生'你可以查找这个决策树,看看你正在寻找的运营商http://reactivex.io/documentation/operators.html#tree 我通常使用skip(1)
答案 6 :(得分:0)
将订阅代码放在ngAfterViewInit方法上,
ngAfterViewInit() {
this.route.queryParams.subscribe(params => {
debugger;
});
}
答案 7 :(得分:0)
以防万一有人在寻找解决方案,我发现了一种很好的解决方法。我想出的解决方案仅涉及对Router实例的NavigationEnd
事件进行订阅。
import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
this.router.events
.subscribe(e => {
if (e.constructor.name === 'NavigationEnd' && this.router.navigated) {
this.route.queryParams
.subscribe(params => {
// do something
})
.unsubscribe();
}
});
}