Angular 2路由器在Resolve中使用BehaviorSubject Observable

时间:2017-02-21 03:40:31

标签: angular angular2-routing observable angular2-observables

我正在尝试使用Resolve设置我的路由器配置,它从BehaviorSubject返回一个Observable。我在角4.0.0-beta8和角2.4.8 +路由器3.4.8

中尝试了这个

这是我的服务:

@Injectable()
export class MyService {
    private _data: BehaviorSubject<Array<string>> = new BehaviorSubject(undefined);

    constructor() {}

    public getData(): Observable<Array<string>> {

        this._data.next(['test1', 'test2', 'test3']);

        let asObservable = this._data.asObservable().delay(1000);
        asObservable.subscribe((myData) => {
            console.log([myData, 'this console message DOES show up']);
        });

        // if I return here, my component's constructor and ngOnInit never fire
        // return asObservable;

        let fakeObservable = Observable.of(['test1', 'test2', 'test3']).delay(1000);
        fakeObservable.subscribe((fakeData) => {
            console.log([fakeData, 'this console message shows up']);
        });

        console.log([asObservable, fakeObservable]);
            /* console log output
            Observable {
                _isScalar: false,
                operator: DelayOperator,
                source: Observable {
                    _isScalar: false,
                    source: BehaviorSubject {
                        _isScalar: false,
                        _value: ['test1', 'test2', 'test3'],
                        closed: false,
                        hasError: false,
                        isStopped: false,
                        observers: Array[1],
                        thrownError: null,
                        value: ['test1', 'test2', 'test3']
                    }
                }
            },
            Observable {
                _isScalar: false,
                operator: DelayOperator,
                source: ScalarObservable {
                    _isScalar: true,
                    scheduler: null,
                    value: ['test1', 'test2', 'test3']
                }
            }
            */

        return fakeObservable; // this WILL reach my component constructor and ngOnInit
    }
}

这是我的决心

@Injectable()
export class MyResolver implements Resolve<Array<string>> {

    constructor(private myService: MyService) {}

    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Array<string>>|undefined {
        return this.myService.getData();
    }
}

这是路由器

RouterModule.forChild([{
    path: 'mypath',
    component: MyComponent,
    resolve: {
        data: MyResolver
    }
}]);

这是组件:

@Component({
    selector: 'my-component',
    template: '<Span>My Component</span>'
})
export class MyComponent implements OnInit {
    constructor(private route: ActivatedRoute) {
        console.log('component constructor');
    }

    ngOnInit(): void {
        console.log(this.route.snapshot.data['data']); // ['test1', 'test2', 'test3']
    }
}

这可能不是设计解决方案和服务之间交互的最佳方式,因此我非常愿意接受建议。但是,如果我不弄清楚为什么BehaviorSubject.asObservable()不起作用,但是模拟的observable确实有用,我可能会发疯。

3 个答案:

答案 0 :(得分:11)

我在一夜之间想到了这一点,并意识到我正在使用解决方法。问题的关键在于路由器期望解析结果最终完成。 BehaviorSubject,即使它一次只有一个值 ,也永远不会完成,因为该值总是可以更改。我将this._data.asObservable()更改为this._data.asObservable().first(),然后就开始工作了。现在看来很明显了!

答案 1 :(得分:1)

好像它是一个错误。

在一个plunker中复制它:https://plnkr.co/edit/XmjC2rJ20VQWCsfncVIc?p=preview

@Injectable()
export class MyService {
    private _data: BehaviorSubject<Array<string>> = new BehaviorSubject(undefined);

    constructor() {
      console.log('created MyService');
      console.log(symbolObservable);
    }

    public getData(): Observable<Array<string>> {

        this._data.next(['test1', 'test2', 'test3']);
        this._data.do(myData => {
          console.log([myData, 'this console message DOES show up']);
        });

        return this._data.delay(1000); // <-- won't work .. maybe an angular bug !!
        return Observable.of(['test']);
    }
}

在github上创建了一个错误:https://github.com/angular/angular/issues/14614

答案 2 :(得分:1)

要使Angular 6+完成解析器中的可热观测,您需要执行以下操作:

return this.myService.getData().pipe(take(1))