The scenario goes as follows:

1. App has root module and it's constructor resolves a Promise and returns a Constant.

@NgModule({
    ....,
    providers : [
    ...
    { 
      provide: SAMPLE_INJECTOR_TOKEN,
      useFactory: sampleFactoryFn(SAMPLE_GLOBAL_CONSTANT.DATA)
    }
       ]
})
export class AppModule {
 constructor() {
  // To be clear: This code runs on window.onLoad(). window argument is actually window object
  setupPromiseAPI(window).then(data => {
   SAMPLE_GLOBAL_CONSTANT.DATA = data; // Once promise resolves this gets expected value. [ Step 3 ]
  }).catch({
      ...
  })

}

2. As this value is an object and also, as it will be available at run time, I have created a Injection Token using "useFactory" and registered at the module level.

// Injector Token    
export SAMPLE_INJECTOR_TOKEN = new InjectionToken<CustomDataType>('SampleInjectorToken');

// Factory Function
export function sampleFactoryFn(data: CustomDataType) {
       return (/* No deps to be passed */): CustomDataType => data; // simply return what ever it received.
            }

3. I have created a global constant initialized to null and passed as argument to the factory function, using which angular is constructing the Injector Token and assigning the value null (initial state) at bootstrap.

export const SAMPLE_GLOBAL_CONSTANT = {
    DATA: null // Initial State
}

4. When promise gets resolved from Step 1, I am capturin the returned constant value from Promise and assiging to this declared Global Constant, as its a constant, obviously that is being updated and available all through the app.

Also, angular documentation states that useFactory provider creates its value lazily, which I am understanding that it will created when its injected. If so, by the time code injects my Injection Token, the global constant is updated. Still the injector token is having null.

Q1. Is there a way, to invoke provider factory function with the updated local state, so that my injector token will have received value?

Q2. As am using global constant, its making the code work, but its not loosely coupled which also making unit testing hard. Are there any alternatives to consume received constant and use it in code, so that it will be loosely coupled?

1

There are 1 best solutions below

0
On

One way, which I resolved is with the usage of Replay Subject from RxJS. First of all, this is a postMessage service wrapped in promise. So, once message was sent (from another app)and promise was resolved (in using app), there has to be a way to hold that send data to use it across the app.

Using the Replay Subject with buffer value of 1, which means to replay the recent one value to the subscription, does produce the last caught value once above scenario is resolved.

Then pass the obtained data to an Angular Service, in subscription, which makes it available across the app.

Now, this makes the required data in the angular service rather than Global Constant, which makes unit testing easy.

NB: Factory Provider doesn't resolve this situation because, angular tries to resolve before the launch happens, which defaults to initialized value (In my case, it was null). So this wont work.

Why not Async Subject ?

As Async Subject gets fired only post completion signal, and the business case which I am solving do have intermediate work to do before declare completion signal. It didn't solve the purpose.