Angular 2 Router Using BehaviorSubject Observable in Resolve

廉价感情. 提交于 2019-12-05 23:53:21

问题


I'm trying to set up my router config using a Resolve that returns an Observable from a BehaviorSubject. I've tried this in both angular 4.0.0-beta8 and angular 2.4.8+router 3.4.8

Here's my service:

@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
    }
}

Here's my resolve

@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();
    }
}

Here's the router

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

And here's the component:

@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']
    }
}

This is probably not the best way of designing the interaction between the resolve and the service, so I'm very open to suggestions there. However, I might go crazy if I don't figure out why BehaviorSubject.asObservable() doesn't work, but the mocked observable does work.


回答1:


I thought about this one overnight, and realized that I was using the resolve incorrectly. The crux of the problem is that the router expects the resolve result to eventually be completed. The BehaviorSubject, even though it only has one value at a time, will never be done, because the value can always change. I changed this._data.asObservable() to this._data.asObservable().first(), and it started working. It seems so obvious now!




回答2:


Seems like it's a bug.

Reproduced it in a 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']);
    }
}

Created a bug on github: https://github.com/angular/angular/issues/14614




回答3:


For Angular 6+ to complete hot observable in resolver you need to do as follows:

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


来源:https://stackoverflow.com/questions/42358227/angular-2-router-using-behaviorsubject-observable-in-resolve

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!