* ngxs: 3.2.0
* @angular/core: 6.1.1
Stackblitz / Github link: https://stackblitz.com/edit/ngxs-simple-xjaodj
Run the repro, read the output and then read the code in the app directory to understand the output.
ngxsOnInit runs before APP_INITIALIZER gets a chance to initialize app services.
Hello from AppState::ngxsOnInit!
Hello from a simple APP_INITIALIZER!
Hello from an asynchronous APP_INITIALIZER!
Hello from AppComponent::ngOnInit!
ngxsOnInit should somehow wait for all provided APP_INITIALIZER functions to complete.
Hello from a simple APP_INITIALIZER!
Hello from an asynchronous APP_INITIALIZER!
Hello from AppState::ngxsOnInit!
Hello from AppComponent::ngOnInit!
I think the correct way to ensure that all APP_INITIALIZER functions have completed is to wait for the first NavigationStart event.
Or even better: provide your own APP_INITIALIZER that runs after every user-defined APP_INITIALIZER.
I think that's why some of my services are not available in custom plugin. Could it be?
Something new about this issue?
@markwhitfeld whether it is breaking changes if will be fixed?
Any fix for this is breaking in the sense that it is a change in observable behavior, which some people may depend on.
I'd love to know the view on fixing this. I have a dynamic app config plugin I wrote that relies on APP_INITIALIZER to download some config data for base URLs before the API services are called. We just started implementing NGXS and our app is blowing up because it can no longer fetch that config data in advance of the store loading up.
In order for this to not be a breaking change we could create another lifecycle hook ngxsAfterInit which gets fired at the correct time.
Would anyone like to attempt a PR for this?
Cc @deebloo @splincode @eranshmil
Are we happy to have another lifecycle hook?
Yeah, that's fine with me.
I think I need to take into integrate with HMR hooks. Maybe ngxsAfterOnInit better give another name ngxsStateChecked.
Lifecycle + HMR integretion
ngxsOnInit -> ngxsHmrAfterOnInit
ngxsStateChecked -> ngxsHmrAfrterStateChecked
@DanaEpp as a solution to your problem why don't you dispatch a ConfigLoaded action when your config has loaded and then respond to this in your State classes.
Today we can use ngxsAfterBootstrap, this will be released in 3.4.0
Thanks @splincode, works great in latest dev build.
https://stackblitz.com/edit/ngxs-simple-rqsxyy
I'm on 3.5.1 and it seems that things are still initialized before APP_INITIALIZER.
Not sure if I'm missing something or if I don't completely understand what this issue's resolution should be?
This is my use case:
I need to initialize Ngxs through a library module that either is imported or not in the apps (monorepo structure)
And I couldn鈥檛 find a way to initialize it properly (the apps that import it should pass further if production is true or not, since a library shouldn't care about the app's environment directly)
But it seems that the initialization still takes place before APP_INITIALIZER resolves
This is my stackblitz: https://stackblitz.com/edit/ngxs-app-initializer?file=src%2Fapp%2Fstore%2Fstore.module.ts
The console will show:
prev state
next state
APP_INITIALIZER
APP_INITIALIZER Resolved
NGXS Logger shouldn't log anymore (but it does)
payload
prev state
next state
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
but it should have production: true (I pass that hardcoded, since I don't have environments in the stackblitz) and developmentMode should be false. Also, the logger shouldn鈥檛 log.
P.S. Because of hmr, you need to reload the right-side browser window every time you make a change (at first I was tempted to think that the problem was resolved because of HMR, I wouldn't get the console logs all the time... but pressing reload shows every time that ngxs initializes before APP_INITIALIZER resolves)
@MrCroft use ngxsAfterBootstrap
https://www.ngxs.io/advanced/life-cycle#ngxsafterbootstrap
About APP_INITIALIZER
https://www.ngxs.io/v/master/advanced/life-cycle#app_initializer-stage
How work NGXS Lifecycle
https://github.com/ngxs/store/blob/master/packages/store/src/internal/lifecycle-state-manager.ts#L28
Thanks @splincode !
Unfortunately, my issue is not inside the state itself...
It's that this line: NgxsModule.forRoot([], { developmentMode: <true or false> }) (and also the plugins initialization) inside the imports of the @NgModule decorator of MyStoreModule is happening before APP_INITIALIZER runs/resolves. And I don't have my boolean value (developmentMode:
The StackBlitz illustrates better what I'm trying to achieve.
I still want to believe that it must be possible to pass async values to initialization logic that happens inside @NgModule decorator. As far as I read, that's what APP_INITIALIZER is for...
Basically, in the provided stackblitz there shouldn't have been any logs from the logger plugin, because in my AppModule is do MyStoreModule.forRoot({ production: true }) which should get to MyStoreModule and to StoreService, which is then used like NgxsLoggerPluginModule.forRoot({ disabled: StoreService.production }) in MyStoreModule's @NgModule decorator
In production you don't need use LoggerPlugin and DevtoolsPlugin
https://www.ngxs.io/v/master/recipes/dynamic-plugins
You can register plugins at compile time
Basically, that's what I'm trying to achieve using disabled: true/false: NgxsLoggerPluginModule.forRoot({ disabled: <boolean value> }).
But this initialization happens inside a library module. And libs aren't/shouldn't be aware of the environment of the app that consumes the library, so I need to somehow tell it what's the right boolean value when I import it in an app:
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { MyStoreModule } from '@my-namespace/store';
import { environment } from '../environments/environment';
@NgModule({
imports: [
// ...
MyStoreModule.forRoot({ production: environment.production })
],
// ...
})
export class AppModule {}
And then in my library's MyStoreModule module I need to somehow get that "production" value that's passed from the app, in the forRoot, and use it inside @NgModule on NgxsLoggerPluginModule.forRoot({ disabled: <that boolean value> })
Anyway, it's a Sunday, you've already spent too much time on Github on my behalf :D sorry about that! I didn't think I'd get a reply today.
EDIT: I was trying to apply the solution presented here for firebase init: https://github.com/nrwl/nx/issues/208#issuecomment-536169371
But can't seem to make it work
you can write your suggestions here
https://github.com/ngxs/store/issues/1417
Most helpful comment
Something new about this issue?