foo-baar
foo-baar

Reputation: 1104

How to control the flow of service calls, using rxjs with Angular 9?

My code looks like this:

userContext: UserContext;
  userContext$ = this.http.get<UserContext>("assets/api/userContext.json", {}).pipe(
      shareReplay(1),
      tap((val: UserContext) => {
        this.userContext = val;
        console.log(this.userContext);
      })
  );
  userAnnouncements$ = this.http.post<Announcement>(this.userAnnouncementsService,
    {
      countryCode: this.userContext.countryCode, 
      companyGroupId: this.userContext.companyGroupId,
    }
  );

As we can clearly see 2nd Service (userAnnouncements$) needs the values received by 1st service (userContext$), now since they are async my userContext: UserContext; is empty while the 2nd service is executed.

I would also need the userContext.companyCode & GroupId for future calls, hence I need a way to keep it somewhere for subsequent calls.

I tried resolver and APP_INT... but then realized this is not the right purpose of both places.

Upvotes: 0

Views: 185

Answers (3)

VRoxa
VRoxa

Reputation: 1051

I was expecting your answer.

If you UserContext is something it should be called once, as I mentioned, you can think of it as a singleton.
Fetch it once, then caches it inside your service.

class MyService {

    private _userContext: UserContext | undefined;

    constructor (private http: BackgroundService) { }

    userContext$ = this.http.get<UserContext>(`url`).then(ctx => {
        console.log(ctx);
        this._userContext = ctx;
        return ctx;
    });

    userAnnouncements$ = (): Promise<Announcement> => 
        this._userContext
            ? this.userAnnouncement(this._userContext)
            : this.userContext$.then(ctx => this.userAnnouncement(ctx));

    private userAnnouncement = (ctx: UserContext): Promise<Announcement> =>
        this.http.post<Announcement>(`url`, {
            countryCode: ctx.countryCode,
            companyGroupIp: ctx.companyGroupIp
        });
}

interface BackgroundService { // aka HttpClient
    get: <TResult>(url: string) => Promise<TResult>;
    post: <TResult>(url: string, body: any) => Promise<TResult>;
}

I just mocked up the HttpClient just for me to compile it properly. You can forget about it.

The point is, you service class has an inner property UserContext. Any call to userContext$ will provide this object and caches inside the service.
If a call to userAnnouncements$ is done, then it uses the cached UserContext. Whenever a call to userAnnouncements$ is done before any call to userContext$, so UserContext is null or undefined, then, a previous call to userContext$ is done to fulfill the property.

_userContext may be static depending on your dependency injection policies. If you want to every service to store their own _userContext, fine. Even if MyService is singleton, then _userContext should not be static. Otherwise, an static _userContext is needed to share across all the service instances.

Moreover, you could overload the userAnnouncements$ to to force a UserContext refetch. That would be interesting, if needed.

Hope it helps.

Upvotes: 1

Ashish Ranjan
Ashish Ranjan

Reputation: 12960

You already are using shareReplay, only thing you have got to do is use the result of this Observable, wherever you want.

userAnnouncements$ = this.userContext$.pipe(mergeMap((userContextData) => {
    return this.http.post<Announcement>('someUrl',
        {
        countryCode: userContextData.countryCode, 
        companyGroupId: userContextData.companyGroupId,
        }
    );
}));

Upvotes: 0

Nabel
Nabel

Reputation: 1956

Not sure if this is what you are asking for but you could use switchMap

userContext: UserContext;
  userContext$ = this.http.get<UserContext>("assets/api/userContext.json", {}).pipe(
      tap((val: UserContext) => {
        this.userContext = val;
        console.log(this.userContext);
      }),
      switchMap((val: UserContext) => {
          return this.http.post<Announcement>(this.userAnnouncementsService, 
          {
              countryCode: val.countryCode,
              companyGroupId: val.companyGroupId
          });
      }
  );

Then you only have one subscription

Upvotes: 0

Related Questions