I am very new to ngrx
, and just trying to get my head around it, and get something working.
I have added ngrx (version 8.3) to my application.
I wish to have a few things in a root state (if possible), and then have separate states for each of my features. I started at the root state, but the selector I have is just never notified.
I have the following actions...
// actions
import { createAction, union } from '@ngrx/store';
import { SecurityTokensState } from './app.reducer';
export const setUrl = createAction(
'[App url] ',
(payload: string) => ({ payload })
);
export const setTokens = createAction(
'[App setSecurityTokens] ',
(payload: SecurityTokensState) => ({ payload })
);
export const actions = union({
setUrl,
setTokens
});
export type ActionsUnion = typeof actions;
And the following reducers..
import * as rootActions from './app.actions';
import { createReducer, on } from '@ngrx/store';
/** Top level state */
export interface State {
/** State to do with Auth */
tokens: SecurityTokensState;
/** General / root app state (eg configuration) */
app: AppState
}
/** App wide general state */
export interface AppState {
url: string;
//extraLogging: boolean;
//offlineExpiry: number;
//offlineTime: number;
}
/** Security token state */
export interface SecurityTokensState {
token: string,
refreshToken: string;
}
const initialState: State = { tokens: undefined, app: { url: ""} };
export function rootReducer(state: State, action: rootActions.ActionsUnion): State {
return reducer(state, action);
}
const reducer = createReducer(
initialState,
on(rootActions.setTokens,
(state, { payload }) => ({ ...state, tokens: payload })
),
on(rootActions.setUrl,
(state, { payload }) => ({ ...state, app: updateUrl(state, payload)}))
)
/** Helper to update the nested url */
const updateUrl = (state: State, payload: string): AppState => {
const updatedApp = { ...state.app };
updatedApp.url = payload;
return updatedApp;
}
And I created the following selector...
import { createFeatureSelector, createSelector } from "@ngrx/store";
import { AppState } from './app.reducer';
const getAppState = createFeatureSelector<AppState>('app');
export const getUrl = createSelector(
getAppState,
state => state.url
);
In app.module, I have the following...
StoreModule.forRoot(rootReducer),
Now, in a component, I have
import * as rootSelectors from '../state/app.selectors';
....
public onUrlBlur(ev : any): void {
let val = ev.target.value;
this.store.dispatch(rootActions.setUrl(val));
}
And I have the code to subscribe to the update
this.subs.sink =
this.store.pipe(select(rootSelectors.getUrl)).subscribe(url => {
this.urlEntered = url
});
Lastly, just as a placeholder, in one of my feature modules, I have added...
StoreModule.forFeature('myfeature1', {})
I see the blur function being called, and in the redux dev tools I see
But for the state, all I see is
And the observable this.store.pipe(select(rootSelectors.getUrl)).subscribe(url => {` never fires
So my root state does just not seem to be in there, and I really can't see what I have done wrong.
Where have I messed up here?
Have added a very similar example (with the same problem) here
When running, and go to the console, can see the following...
selector.ts:610 The feature name "appRoot" does not exist in the state, therefore createFeatureSelector cannot access it. Be sure it is imported in a loaded module using StoreModule.forRoot('appRoot', ...) or StoreModule.forFeature('appRoot', ...). If the
What I don't understand if how to use the StoreModule.forRoot(rootReducer ),
In the error, it suggests using a string... eg StoreModule.forRoot('app', rootReducer ),
but this gives a syntax error.
If I do the following:
StoreModule.forRoot({appRoot: rootReducer} ),
I get a nested state:
But just passing the reducer:
StoreModule.forRoot(rootReducer ),
I get no state:
All the examples I see use just feature states, but I have a number of settings that are just application wide, and not in a feature module.
Also, as this state is not in a feature module, I am not sure if I should be using the createFeatureSelector:
const getAppState = createFeatureSelector<AppState>('appRoot');
The StoreModule.forRoot()
function expects a ActionReducerMap
, not a reducer function.
See the docs for more info.
To resolve the nested state problem, in your case this would look as follows:
StoreModule.forRoot({
tokens: tokensReducer,
appRoot: appRootReducer
})
or you can do:
StoreModule.forRoot({}),
StoreModule.forFeate('appRoot', appRootReducer)