Search code examples
angularangular-http-interceptorsangular-di

How to pass dynamic variable to http_interceptor from a service


I have multiple angular projects and each project has a service to call the same API. I've been adding a header to request to determine from which app the request has been sent. So I've been trying to create a library having a base service class to set header value


    @Injectable({ providedIn: "root" })
    export class AppbaseService {
        scope: string; 
        constructor(@Inject(forwardRef(()=>'scope')) scope:string) { 
            this.scope = scope;
        }
        getScope():string{
            return this.scope;
        }
    }

and a http interceptor to append that dynamic value to request as a header


    @Injectable()
    export class ScopeInterceptor implements HttpInterceptor {
    
        constructor(private appBaseService: AppbaseService) {
        }
    
        intercept(req: HttpRequest<any>,
            next: HttpHandler): Observable<HttpEvent<any>> {
    
            const clonedRequest = req.clone({
                headers: req.headers.append(
                    'Scope',
                    this.appBaseService.scope
                )
            });
            return next.handle(clonedRequest);
        }
    }

and finally a factory to set the dynamic value to provider


    export function appValueFactory(baseService: AppbaseService) {
        console.log(baseService);
        return baseService.getScope();
    }

but I couldn't figure out how to set the providers in the module and getting below error, which is not very helpful.


    Uncaught Error: Can't resolve all parameters for qI: (?).
        at Jt (main.js:1)
        at e._getDependenciesMetadata (main.js:1)
        at e._getFactoryMetadata (main.js:1)
        at e.getProviderMetadata (main.js:1)
        at main.js:1
        at Array.forEach (<anonymous>)
        at e._getProvidersMetadata (main.js:1)
        at main.js:1
        at Array.forEach (<anonymous>)
        at e._getProvidersMetadata (main.js:1)

Can someone please point me in the right direction?

Thank you.


Solution

  • First of all, ¿what is this for: @Inject(forwardRef(()=>'scope'))? In constructor you should inject services and scope is a string.

    I understand scope is a string identifier of the current app, so the easy solution is to add that string directly in your interceptor.

    If you want you use a service to get that string, the problem will be inside AppbaseService, as scope value is asynchronous (it is filled in the constructor) so, if your code tries to invoke ScopeInterceptor before AppbaseService is created, scope will be undefined.

    To fix this make the service return an Observable (we'll asume scopeService.getAppScope() return this app's scope string)

        export class AppbaseService {
            scope = new BehaviorSubject<string>('');
            constructor(private scopeService: ScopeService) { 
                this.scope.next(scopeService.getAppScope());
            }
            getScope$(): Observable<string> {
                return this.scope.asObservable();
            }
        }
    

    And from the interceptor consume this async function and returns an Observable too:

            intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
                return this.appBaseService.getScope$().pipe(
                  filter(scope => !!scope), 
                  take(1), 
                  switchMap(scope => {
                    const clonedRequest = req.clone({
                      headers: req.headers.append('Scope',scope)
                    });
                    return next.handle(clonedRequest);
                }))
            }
    

    Explanation of rxjs functions:

    • The filter is used to filter undefined values: when scope is not yet defined, do not continue with the pipe.
    • take(1): once the value is received, consume the observable only once and then complete.
    • switchMap: takes the value from scope observable and returns another observable with the HttpEvent