-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AOT and reducer factories in v4 #116
Comments
I can confirm that I am having similar issues. Here is relevant code in my project: I haven't dug too much into it yet. |
That's a limitation from the angular AOT compiler. It is partially explained here However, that explanation refers to the The problem you'r having is with the This workaround should probably work: export const reducers: ActionReducerMap<RootState> = {};
reducers.organization = fromOrganization.createReducer(ORGANIZATION);
reducers.user = fromUser.createReducer(USER); The team will probably need to support a reducer factory function for I did see that there is a |
@shlomiassaf That sort of worked--thanks. I had to remove the equivalent of It is a weird bug all round, especially the fact that compilation didn't always seem to fail. |
You can use an Injection token instead. See ngrx/store#444 (comment) |
@brandonroberts ah, that looks cleaner! I'll give it a try. |
Has anyone tested whether the example app in this repo works with AOT? |
I did not check but I can't get this to work.... The |
I have not tested either (trying to figure out how to build it right now) but I noticed this line of code in the app module:
I forget exactly what the reasoning was, but I remember debugging my code for hours trying to figure out why my app was building fine w/o AOT but was failing with the AOT flag enabled. I finally realized that my code was failing to build because I was dynamically enabling/disabling certain imports in my |
Ok, ran this repo AOT. I can confirm that the using an To run the example app AOT you need to:
yarn add @ngrx/store @ngrx/effects @ngrx/router-store @ngrx/store-devtools Yes... we need to do that so we wont need to get crazy with AOT path mapping for the modules.
|
Also, not sure if this has already been mentioned, but there are some docs on AOT compilation with v4 here: https://github.com/ngrx/platform/blob/master/docs/store/api.md#initial-state-and-ahead-of-time-aot-compilation |
This looks like heavy DI stuff. At the point where the providers needs to resolve the user provided token for the reducers is not present, kind of... It is present (with its value) in the module definition and the providers for the module definition. It will be but not at this point, I tried injection it into the constructor of the The picture above is that moment when StoreModule needs to resolve it. { provide: _INITIAL_REDUCERS, useValue: reducers },
{
provide: INITIAL_REDUCERS,
useFactory: _initialReducersFactory,
deps: [_INITIAL_REDUCERS, Injector],
}, export function _initialReducersFactory(reducers: any, injector: Injector): any {
return reducers instanceof InjectionToken
? injector.get(reducers)
: reducers
;
} Put a breaking point at the function when it starts, and thats the state for that image |
Ok, with more digging into the problem i'm not sure it's a bug in ngrx I managed to get this working using the fix I set in the last comment + a workaround. This splits into multiple issues that together cause this. Why the fix (_initialReducersFactory): The AOT compiler does not handle metadata logic statement properlyThe reducers instanceof InjectionToken
? { provide: INITIAL_REDUCERS, useExisting: reducers }
: { provide: INITIAL_REDUCERS, useValue: reducers }, The AOT compiler is aware of that since this gets reflected in the When we examine the AOT compiled code: __WEBPACK_IMPORTED_MODULE_0__angular_core__["d"/* ɵmpd */](
256,
__WEBPACK_IMPORTED_MODULE_5__ngrx_store__["l"/* INITIAL_REDUCERS */],
__WEBPACK_IMPORTED_MODULE_1__app_app_module__["b"/* REDUCERS_TOKEN */],
[]
); The above is the AOT representation of the logical statement above it. Since The flag
It means that AOT yielded I think we need firepower here, @mhevery @tbosch @robwormald WorkaroundThe workaround (after applying the fix) is just making sure that the provider is available before Setting the provider at the So just make sure it's evaluated before: export const REDUCERS_TOKEN = new InjectionToken<ActionReducerMap<any>>('Registered Reducers');
@NgModule({
providers: [
{ provide: REDUCERS_TOKEN, useValue: reducers }
]
})
export class TempModule {
constructor(@Inject(REDUCERS_TOKEN) r: any) {
console.log(r);
}
} And then set this module in the AppRoot imports.
|
Workaround until this is resolved:export const reducerToken = new InjectionToken<ActionReducerMap<State>>('Registered Reducers');
export const reducerProvider = [
{ provide: reducerToken, useValue: reducers }
]; After creating the token and provider act as if the token is the reducer map Object.assign(reducerToken, reducers); Will do the job for now. |
Thanks for the workarounds. With With object properties moving: |
FYI, the InjectorToken workaround currently only works for reducers that are imported in forRoot, not forFeature. see #141. |
Want to emphasise that this is a "hack", tomorrow the angular team can decide to "freeze" the injection token instance... who knows... so this should be fixed. |
Hmm... in that case, should we implement the use of InjectionToken for forFeature, or is there some other way around this problem that would be less hacky? |
Well, that's not much of a worry for now... Using the workaround ( Once this issue is fixed you just remove the
|
@shlomiassaf I'm not sure I understand the |
When using @brandonroberts suggestion to use DI export const reducerToken = new InjectionToken<ActionReducerMap<State>>('Registered Reducers');
export const reducerProvider = [
{ provide: reducerToken, useValue: reducers }
]; We face an issue, it does not work on AOT. What happens in AOT is that due to a bug the There's no easy way to fix that without starting to change the whole logic... |
@shlomiassaf OK, I still don't understand. Where are you calling |
export const REDUCERS_TOKEN = new InjectionToken<ActionReducerMap<State>>('Registered Reducers');
const reducers = {
auth: () => {},
nav: () => {},
...
}
// WORKAROUND HERE
object.assign(REDUCERS_TOKEN, reducers)
// WORKAROUND HERE
@NgModule({
import: [
StoreModule.forRoot(REDUCERS_TOKEN),
],
providers: [
{ provide: REDUCERS_TOKEN, useValue: reducers }
]
})
export class AppModule { } |
Thank you. Sorry if I seemed obtuse. It seems to work, although I haven't tested with AOT. |
Sure, all good. I did test with AOT, for me it works. FYI: With JIT the |
@shlomiassaf Is I define |
If i understand it right: if provided value is
else INITIAL_REDUCERS will be created with this constructor, which is, again, platform/modules/store/src/tokens.ts Line 11 in 1bbd5bf
|
@ValeryVS @shlomiassaf For AOT, I need Without that line, ngrx/store doesn't get the reducer object. |
@ValeryVS I tried your repo. It doesn't work with |
@shlomiassaf Thanks for the hack but from my end, |
I used metareducers based on this: and was getting AOT errors when I ported to v4. Injecting with the Object.assign hack works, as well as the effects. Didn't work without the Object.assign. |
Another option besides injecting a reducer token if you're using some type of factory function is to wrap that factory in an exported function. Example from opening post below. I'll close this with a PR to the docs about reducer factories. export function organizationReducer(state, action): fromOrganization.State {
return fromOrganization.createReducer(ORGANIZATION)(state, action);
}
export function userReducer(state, action): fromUser.State {
return fromUser.createReducer(USER)(state, action);
}
export const reducers: ActionReducerMap<RootState> = {
organization: organizationReducer,
user: userReducer,
}; |
@shlomiassaf @maxisam @brandonroberts @MikeRyanDev @brandonroberts Also, while app is compiled with AOT and @ngrx nightly builds, there is some runtime error.
|
@ValeryVS I don't know how it worked for you before @brandonroberts fix, it strange since the AOT compilation will not allow that. After the fix it should work without |
It is, probably, some other bug. But because of mentioned in previous post |
I had this |
I'm experimenting with factories for ngrx.
Here is example app.
https://github.com/ValeryVS/angular-storage-experiment
with v2
https://github.com/ValeryVS/angular-storage-experiment/blob/16febfb4d141476dfefe97b5de4b8f90d318d68b/src/app/store/reducer.ts
with v4
https://github.com/ValeryVS/angular-storage-experiment/blob/46c20b987369b0017435f37fada4febdcf1e6937/src/app/store/reducer.ts
However, in v4 version I have AOT error.
ERROR in Error encountered resolving symbol values statically. Expression form not supported (position 16:7 in the original .ts file), resolving symbol createReducer in /Users/valery/Projects/storage-experiment/src/app/store/organization/reducers/index.ts, resolving symbol compileReducers in /Users/valery/Projects/storage-experiment/src/app/store/reducer.ts, resolving symbol AppModule in /Users/valery/Projects/storage-experiment/src/app/app.module.ts, resolving symbol AppModule in /Users/valery/Projects/storage-experiment/src/app/app.module.ts, resolving symbol AppModule in /Users/valery/Projects/storage-experiment/src/app/app.module.ts
Can you give an advice, what to change?
The text was updated successfully, but these errors were encountered: