How do you prevent a BehaviourSubject from being consumed before it's defined?

久未见 提交于 2019-12-23 17:02:37

问题


I'm working on a user service that keeps track of users by their user_id. It first checks if user_id is present in their cookies, if it isn't it makes a get request to the api to create a new one and returns the id in the response. My problem is that the user_id is being consumed before the response is completed. I have two questions:

  1. The BehaviourSubject is being consumed before it's even defined as a new BehaviourService, components that consume it are calling .subscribe() on undefined as a result and the app is crashing.
  2. Is the really necessary for me to subscribe to the user_id every single time I want to retrieve it? The code is turning into hell to work with since i'm subscribing to the behavioursubject first and then inside the subscribe method I'm writing my code. I can't figure out a better way to go about it, and from the texts I've read they mention that linking streams is the right approach, but this just feels very wrong.

Here's a simplified example of what I'm doing

get ruid or create one - user.service.ts

constructor(...) {
    public ruid: BehaviorSubject<any>;
    if(!Cookies.get('ruid')) {
        this.http.get(url).subscribe(
            (value) => {
                this.ruid = new BehaviorSubject(value)
                Cookies.set('ruid', value)
            }
        )
    } else {
        this.ruid = new BehaviorSubject(Cookie.get('ruid'));
    }
}

use ruid in component

constructor(private userService: UserService) {
    data;
    this.userService.ruid.subscribe(
        (value) => {
            this.data = this.http.get(url + value).map(res => res.json())
        }
    );
}

回答1:


I believe what you need here is to setup resolver on your route. See my example from real project below:

resolver.ts

@Injectable()
export class LocationResolver implements Resolve<any> {
    constructor(private  locationsApiService: LocationsApiService, private appStorage: AppStorage, private authService: AuthService) {

    }

    resolve(route: ActivatedRouteSnapshot): Observable<any> {
        return new Observable((observer) => {
            const sessionData = this.authService.getSessionData();

            if (!sessionData.Location) {
                this.locationsApiService.getUserLocations().subscribe(
                    locations => {
                        sessionData.Location = locations[0].Name;
                        sessionData.LocationId = locations[0].Id;

                        this.authService.updateSessionData(sessionData);
                        this.appStorage.set(AppConstants.storage.userLocations, locations);

                        observer.next(sessionData.LocationId);
                        observer.complete();
                    }
                );
            } else {
                observer.next(sessionData.LocationId);
                observer.complete();
            }
        });
    }
}

Routes

{
        path: 'app',
        component: MainComponent,
        canActivate: [AuthGuard],
        resolve: {
            locationId: LocationResolver
        },
        children: [...]
}

Basically what it does is not resolving route up until it gets the necessary data. In my case I need to query locationId and provide it to every call after login. So resolver either looks for it in LocalStorage or doing a call to the API and then sets the location.

UPD: In your case you can put such resolver on the route that subscribes to BehaviorSubject and it will not get instantiated and subscribed until you finish all the API calls.



来源:https://stackoverflow.com/questions/47170450/how-do-you-prevent-a-behavioursubject-from-being-consumed-before-its-defined

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