How to read query parameters in a APP_INITIALIZER service in angular4?
Asked Answered
A

5

7

I have a ConfigService written to get the configuration details before the angular app bootstraps.

The following code is written in the app.module.ts this code works fine and I am able to load the configs before the app loads.

...
providers: [
    {
        provide: APP_INITIALIZER,
        useFactory: configServiceFactory,
        deps: [ConfigService],
        multi: true
    }
]
...

However, now I wanted to pass a payload to my config API which I have to read from the query parameters.

I tried the following but it throws

...
@Injectable()
export class ConfigService {

    private _configData: any;

    constructor(
        private _http: Http,
        private _activatedRoute: ActivatedRoute
) {
}
...

How do I read the query-parameters inside an APP_INITIALIZER service?

Armendariz answered 7/8, 2017 at 19:7 Comment(1)
APP_INITIALIZER is executed before the application is bootstrapped, Router is not initialized yet. Hooking into the Angular bootstrap process and How to manually bootstrap an Angular application might be of help to youRue
O
9

FWIW, All the methods you need are already provided on the window object.

I recently had to check whether a query parameter of foo was passed on the URL. Here is what I ended up doing:

export class ConfigService {
    constructor(/* your dependencies */) {
        // remember that your dependencies must be manually added
        // to your deps: [] property, ALL OF THEM (direct and indirect)
    }

    initialize() {
        // remember that constructors should not contain business logic
        // instead, call this during the APP_INITIALIZER part

        const url = new URL(window.location.href);
        const foo = url.searchParams.get('foo');

        // your business logic based on foo
    }
}

URL seems to be fairly well supported.

Octroi answered 5/2, 2019 at 23:37 Comment(1)
Your answer solved my problem after a few hours of experimenting, thank you!Thigpen
M
3
export class BootstrapService {
  private getParameterByName(name) {
    const url = window.location.href;
    name = name.replace(/[\[\]]/g, '\\$&');
    const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, ' '));
  }

  public load(): Promise<void> {
    const token = this.getParameterByName('token');
    // todo something with token
    return Promise.resolve();
  }
}
Monochromatic answered 8/8, 2017 at 6:49 Comment(2)
Try to give an complete answer instead of just code. What exactly does it do? Could you break it down into smaller chunks?Preen
it's pretty simple; we have a function called getParameterByName which is vanilla javascript. getParameterByName return the query params from the current URLMonochromatic
T
1

If you want to stick with the Angular way, I would say to use a resolver. Read more here

Tallula answered 9/9, 2020 at 7:58 Comment(0)
V
0

I had the same problem and I solved it using the Router. It is a correct statement that the route gets initialized (and activated) after the app initialization, but that does not make it impossible to subscribe to those events. The only caveat is that your handler will execute later on when those events occur.

Obviously, this approach works with the assumption that you don't need to wait for this handling in your app initialization routine and you can let the route continue initializing. Otherwise, if your initializer needs to complete before the route gets initialized, then you can't use the Router (that can make your app initializer hang forever).

Here is what will work in the app initializer to get a value from the query parameter named "paramName" using Router.


export class ConfigService {

  constructor(
    private router: Router
  ) { }

  init(): void {
    this.router.events.subscribe(e => {
      if (e instanceof ActivationEnd) {
          console.log(e.snapshot.queryParamMap.get('paramName'));
      }
    });
    
  }
}

My "APP_INITIALIZER" calls the init() function above. But you could theoretically do the same in the constructor. I just don't consider doing stuff in a constructor a good design.

Venus answered 7/11, 2022 at 22:11 Comment(1)
This is a good solution, however, what if you have an internal rerouting system that will remove the parameters. on ActivationEnd you will not have the parameters anymore. but this is correct solution though!Nonalignment
N
0

Here is my 2 cents hopefully it will make sense. This will work if you have an internal rerouting at the top level, so you will loose the params before ActivatedRoute has finished loading. This will maintain the "Angular Way"

Following @Tengiz example:

export class ConfigService {
  public params: Params = {};

  constructor(private location: Location, private router: Router) {}

  public init(): Prmise<void> {
    return new Promise(resolve => {
      const url = this.router.parseUrl(this.location.path());
      this.params = url.queryParams;
      resolve();
    })
  }
}

I am using returning a Promise<void> as it is recommended using with APP_INITIALIZER.

parseUrl() Will converto to a URL Tree. https://angular.io/api/router/Router#parseUrl

Nonalignment answered 3/3, 2023 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.