A. Morel
A. Morel

Reputation: 10384

How to wait for a http request to finish from another component?

enter image description here

The call of GetData from the controller of a component is done too early, I would like it to wait for the end of the identification before triggering.

When the page is loading, there is a call to the server for identification:

return this.http.get<AuthInfo>('.../Auth/login').pipe(
  map((authInfo: AuthInfo) => {
    // ... authentification completed
    return authInfo;
  }),
);

Then, from several other components, they will be able to call the server : (with a JWT token previously provided)

this.serviceA.methodB().subscribe(
  result => { ... }
)
methodB(): Observable<any> {
  return this.http.get<any>('.../Data/GetData').pipe(
    map(result => {
            ...
      return result;
    }),
  );
}

I want the methodB to be called only if the identification is finished.

So if any method is called, it must be able to pause if the identification process is not finished.

The difficulty here is that the methodB is called from other places and during or after the execution of the login method.

Is this possible with RxJs?

Upvotes: 0

Views: 616

Answers (2)

Guerric P
Guerric P

Reputation: 31835

You could expose the authentication information in a BehaviorSubject:

class AuthenticationService {
  private subject = new BehaviorSubject(null);
  public info = subject.asObservable();

  authenticate() {
    this.http.get<AuthInfo>('.../Auth/login').subscribe(x => this.subject.next(x), e => this.subject.error(e))
  }
}

Call authenticate wherever you need in your app (e.g: on a login button)

Then I suggest you to use an interceptor in order to make sure that requests are authenticated before they are sent:

class AuthenticationInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return authenticationService.info.pipe(
      tap(x => /* If you need to attach the JWT to an HTTP header, do it there */),
      switchMap(() => next.handle(req))
    )
  }
}

Upvotes: 2

Maximilian Ochs
Maximilian Ochs

Reputation: 141

You could provide the JWT-Token as an ReplaySubject within some kind of service (probably your AuthenticationService, if you have one). Then you could use an HttpInterceptor to intercept every HttpRequest from your application and add the JWT Token to the Request Headers.

class AuthenticationService {
  public token = new ReplaySubject<string>(1)

  public login() {
    // your login logic here
    this.token.next(theToken)
  }
}
class AuthInterceptor implements HttpInterceptor {
  constructor (private authService: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.authService.token.pipe(
      mergeMap(token => {
        req = req.clone({
          headers: new HttpHeaders({
            'Authorization': `bearer ${token}`,
          })
        });
        return next.handle(req);
      })
    )
  }
}

Please note: I haven't tested this code yet and I'm not 100% sure if the ReplaySubject is the right choice here. But using an HttpInterceptor and a shared service for your token should be the way to go.

You probably also need some additional logic in your AuthInterceptor to not intercept the login request and other requests in your application that don't need the token.

For instructions on how to provide and use interceptors, see the Angular Http Client Guide.

Upvotes: 1

Related Questions