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

Angular 2 Router Using BehaviorSubject Observable in Resolve

我正在尝试使用 returns 来自 BehaviorSubject 的 Observable 的 Resolve 来设置我的路由器配置。我在 angular 4.0.0-beta8 和 angular 2.4.8+router 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 确实起作用,我可能会发疯。

似乎是一个错误。

在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

我想了一夜,发现我用错了resolve。问题的症结在于路由器期望解析结果最终完成。 BehaviorSubject,即使它一次只有一个值,也永远不会完成,因为值总是可以改变的。我将 this._data.asObservable() 更改为 this._data.asObservable().first(),它开始工作了。现在看起来很明显!

对于 Angular 6+ 要在解析器中完成可观察,您需要执行以下操作:

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

// or

this.myService.getData().pipe(first())