问题
I am attempting to migrate an existing application from ngrx v2 to v4, and am having a bit of trouble with a scenario I do not see covered in the migration document. The migration document has you remove @ngrx/core, but I am not sure what to do with my reducers that import from @ngrx/core/add/operator/select and select from an Observable. I am getting the error "Property 'select' does not exist on type 'Observable'" from all of the reducers.
/actions/action-with-payload.ts -- Workaround for the migration
import { Action } from '@ngrx/store';
export class ActionWithPayload implements Action {
type: string;
payload?: any;
}
/actions/users-list.ts
export class UsersListActions {
static LOAD_USERS_LIST = '[UserManagement/Users List] Load Users List';
loadUsersList(): ActionWithPayload {
return {
type: UsersListActions.LOAD_USERS_LIST
};
}
static LOAD_USERS_LIST_SUCCESS = '[UserManagement/Users List] Load Users List Success';
loadUsersListSuccess(users: User[]): ActionWithPayload {
return {
type: UsersListActions.LOAD_USERS_LIST_SUCCESS,
payload: users
};
}
}
/reducers/users-list.ts
export interface UsersListState {
loaded: boolean;
loading: boolean;
entities: User[];
}
const initialState: UsersListState = {
loaded: false,
loading: false,
entities: [],
}
export default function (state = initialState, action: ActionWithPayload): UsersListState {
switch (action.type) {
case UsersListActions.LOAD_USERS_LIST:
return Object.assign({}, state, {
loading: true
});
case UsersListActions.LOAD_USERS_LIST_SUCCESS:
return Object.assign({}, state, {
loaded: true,
loading: false,
entities: action.payload
});
default:
return state;
}
};
export function getLoaded() {
return (state$: Observable<UsersListState>) => state$
.select(s => s.loaded);
}
export function getLoading() {
return (state$: Observable<UsersListState>) => state$
.select(s => s.loading);
}
export function getUsers() {
return (state$: Observable<UsersListState>) => state$
.select(s => s.entities);
}
/reducers/index.ts
import usersListReducer, * as fromUsers from './users-list';
export interface UserManagementState {
usersList: fromUsers.UsersListState,
};
export { usersListReducer }
export function getUsersListState() {
return (state$: Observable<UserManagementState>) => state$
.select(s => s.usersList);
}
export function getUsers() {
return compose(fromUsers.getUsers(), getUsersListState());
}
export function getUsersLoaded() {
return compose(fromUsers.getLoaded(), getUsersListState());
}
export function getUsersLoading() {
return compose(fromUsers.getLoading(), getUsersListState());
}
/pages/user-list.page.ts
export class UserListPage {
private users$: Observable<User[]>;
private usersLoading$: Observable<boolean>;
constructor(
private store: Store<UserManagementState>,
) {
this.users$ = store.let(getUsers());
this.usersLoading$ = store.let(getUsersLoading());
}
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { usersListReducer } from '/reducers';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
SharedModule.forRoot(),
StoreModule.provideStore({
usersList: usersListReducer,
...
})
],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [
...
]
})
export class AppModule { }
回答1:
I am not sure why you need to import select
. I never did that even in version 2. I will try to show how the code should look like for a single entity.
import {
Action,
ActionReducerMap,
createFeatureSelector,
createSelector
} from '@ngrx/store';
export function userReducer(state = initialState, action: ActionWithPayload): UsersListState {
switch (action.type) {
case UsersListActions.LOAD_USERS_LIST:
return Object.assign({}, state, {
loading: true
});
case UsersListActions.LOAD_USERS_LIST_SUCCESS:
return Object.assign({}, state, {
loaded: true,
loading: false,
entities: action.payload
});
default:
return state;
}
};
export const getLoadedState = (state: UsersListState) => state.loaded;
export const getLoadingState = (state: UsersListState) => state.loading;
export const getEntitiesState = (state: UsersListState) => state.entities;
export userStateSelector = createFeatureSelector<UsersListState>('users');
export const loadedState = createSelector(userStateSelector, getLoadedState);
export const loadingState = createSelector(userStateSelector, getLoadingState);
export const userState = createSelector(userStateSelector, getEntitiesState);
module
imports: StoreModule.forFeature('users', userReducer);
In component use select like this :
public users$: Observable<User[]>;
constructor(private userStore: Store<UsersListState>){
this.users$ = this.userStore.select(userState);
}
There might be lot of compilation errors, but this is just a pseudocode to explain you how things work.
You can check https://github.com/ngrx/platform/tree/master/example-app for detailed example. But personally I struggled a lot to understand how it works. I will probably write a blog this weekend on this if you are interested.
回答2:
Create an interface which contains all the states as its property as below,
export interface CompanyAppStore {
userState: UsersListState;
employeeState: EmployesListState;
}
Create a reducer factory which is a collection of all the reducers as below,
export const reducers: ActionReducerMap<CompanyAppStore> = {
userState: userStateReducer.reducer,
employeeState: employeeStateReducer.reducer
};
Access the reducers by importing them as below,
import * as userStateReducer from './user.state.reducer';
import * as employeeStateReducer from './employee.state.reducer';
Your component can be injected with the app interface which was created in the step 1 as
constructor(private appStore: Store<CompanyAppStore>){ }
Now you can select a particular state using
this.appStore
.select(states => states.userState)
.subscribe(..)
来源:https://stackoverflow.com/questions/45945602/angular-ngrx-migration-error-property-select-does-not-exist-on-type-observab