Angular2查询参数订阅引发两次

luk*_*fer 25 rxjs angular2-routing angular

尝试处理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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码输出: 产量

Plunker(你需要将它弹出到一个新窗口并添加一个查询字符串?test=test).

问题

  1. 有两件事我做错了两次火吗?
  2. 我不能只用条件忽略空对象,因为这是我们需要验证现有身份验证令牌的场景 - 是否有另一种方法来处理这不是一个完整的黑客攻击?

Est*_*ask 19

路由器可观察量(作为另一个答案提及)BehaviorSubject主题,它们与常规RxJS Subject或Angular 2的不同之处EventEmitter在于它们具有推送到序列的初始值(在该情况下为空对象queryParams).

通常,期望订阅初始化逻辑的可能性.

skip操作员可以跳过初始值.

this._route.queryParams
.skip(1)
.subscribe(params => ...);
Run Code Online (Sandbox Code Playgroud)

但更自然的方法是过滤掉所有不相关的参数(初始params属于这一类).authorization_code也可以使用distinctUntilChanged运算符过滤重复值,以避免对后端进行不必要的调用.

this._route.queryParams
.filter(params => 'authorization_code' in params)
.map(params => params.authorization_code)
.distinctUntilChanged()
.subscribe(authCode => ...);
Run Code Online (Sandbox Code Playgroud)

请注意,Angular 2导入有限数量的RxJS运算符(至少map在这种情况下@angular/router).如果rxjs/Rx未使用完整捆绑包,则可能需要导入正在使用的额外运算符(filter,distinctUntilChanged)import 'rxjs/add/operator/<operator_name>'.

  • 在第一个示例中,如果没有queryParams,则将跳过第一个"空"对象,并且永远不会调用订阅块. (6认同)
  • Skip 现在应该放置在管道内部: .pipe(skip(1)).subscribe (2认同)

ero*_*osb 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;
  }
Run Code Online (Sandbox Code Playgroud)


小智 5

您可以等到 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.route.queryParams.subscribe(params => { 
               console.log(params);
             });

           }
       }); 

    }
Run Code Online (Sandbox Code Playgroud)


Jaz*_*uzz 5

以防万一有人正在寻找解决方案,我找到了一个效果很好的解决方法。我提出的解决方案仅涉及对NavigationEndRouter 实例的事件进行订阅。

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();
        }
      });
  }
Run Code Online (Sandbox Code Playgroud)