I have a problem.
I'm using NgRx to handle states for my application, so I'm creating several reducers that contain the admin users, the companies that manage those users, survey types for those companies, etc.
So I have a Reducer for each one, I tried to create several StoreModule.forFeature but I noticed that I had an error like it always tried to access the featureKey of the "admin users" even if it indicated another featureKey (for example, companyStateFeatureKey...), I was reading and from what I understood it was for the reducers and that they had to be mapped with the ActionReducerMap, so I did it, it worked in the sense that it saves the data in the store but I don't know how to access it because it creates several nested "admins" properties then if I do "admin.admins" it gives me an error because of the data model, so I would like to know if there is a way to create several featureReducers or how to access that data that has many nested.
Here is part of the code:
admin.actions.ts
import { createAction, props } from '@ngrx/store';
import { Admin } from "../../components/models";
export const GET_ADMIN = createAction('[ADMIN PAGE] Init');
export const GET_ADMINS_SUCCESS = createAction(
'[ADMIN PAGE] Admins Success',
props<{ admins: Admin[] }>()
);
export const GET_ADMINS_ERROR = createAction(
'[ADMIN PAGE] Admins Error',
props<{ error: string }>()
);
admin.effects.ts
import { Injectable } from "@angular/core";
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, of, exhaustMap } from 'rxjs';
import { AdminService } from '../../services/admin.service';
import { GET_ADMIN, GET_ADMINS_ERROR, GET_ADMINS_SUCCESS } from '../actions/admin.actions';
@Injectable()
export class AdminsEffect {
constructor(
private actions$: Actions,
private adminService: AdminService,
) {}
loadAllAdmins$ = createEffect(() =>
this.actions$.pipe(
ofType(GET_ADMIN),
exhaustMap((initAction) => {
return this.adminService.getAllAdmins().pipe(
map(admins => GET_ADMINS_SUCCESS({admins})),
catchError(() => of(GET_ADMINS_ERROR({error: "There was an error getting the admins"}))),
);
})
)
)
};
admin.reducers.ts
import { Action, createReducer, on } from "@ngrx/store";
import { Admin } from "../../components/models/Admin";
import { GET_ADMINS_SUCCESS } from "../actions/admin.actions";
export interface AdminState {
admins: Admin[];
}
const initialAdminState: AdminState = {
admins: [],
};
export const adminReducer = createReducer(
initialAdminState,
on(GET_ADMINS_SUCCESS, (state, {admins}) => ({
...state,
admins,
}))
);
admin.selectors.ts
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { adminFeatureKey } from '../admin.state';
import { AdminState } from '../reducers/admin.reducers';
export const selectAdmins = createFeatureSelector<AdminState>(adminFeatureKey);
export const admins = createSelector(
selectAdmins,
(selectAdmins) => selectAdmins.admins
)
admin.state.ts
import { ActionReducerMap } from '@ngrx/store';
import { AdminState, adminReducer } from './reducers/admin.reducers';
export interface AdminFlowState {
admins: AdminState;
}
export const reducers: ActionReducerMap <AdminFlowState> = {
admins: adminReducer
}
export const adminFeatureKey = "adminState"
admin.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from 'src/environments/environment';
import { CreateAdmin } from '../components/interfaces/CreateAdmin';
import { Admin } from "../components/models/Admin";
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AdminService {
serverURL = environment.serverURL;
constructor(private http: HttpClient) { }
getAllAdmins(): Observable<Admin[]> {
return this.http.get<Admin[]>(`${this.serverURL}/admins`)
}
...
}
Admin.ts
export interface Admin {
code: string;
name: string;
fsbs: boolean;
createdIp: string;
createdAt: Date;
updatedIp: string;
updatedAt: Date;
logo?: Blob;
pwd?: string;
}
admin.module.ts
...
imports: [
CommonModule,
MaterialModule,
NgxChartsModule,
AdminRoutingModule,
StoreModule.forFeature(adminFeatureKey, reducers),
EffectsModule.forFeature([AdminsEffect]),
ReactiveFormsModule,
FormsModule
],
...
admins-table.component.ts
import { Component, ViewChild } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatTableDataSource } from '@angular/material/table';
import { MatSort } from '@angular/material/sort';
import { Admin } from "../models/Admin";
import { select, Store } from '@ngrx/store';
import { admins } from '../../store/selectors/admin.selectors';
import { GET_ADMIN } from '../../store/actions/admin.actions';
@Component({
selector: 'app-admins-table',
templateUrl: '../views/admins-table.component.html',
styleUrls: ['../styles/admins-table.component.scss']
})
export class AdminsTableComponent {
columnas: string[] = ['code', 'name', 'fsbs', 'createdIp', 'createdAt', 'updatedIp', 'updatedAt', 'logo', 'edit'];
admins: Admin[] = [];
dataSource: any;
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
@ViewChild(MatSort, { static: true }) sort!: MatSort;
constructor(private store: Store) {}
ngOnInit() {
this.store.pipe(select(admins))
.subscribe(admin => {
console.log(admin);
this.store.dispatch(GET_ADMIN());
this.admins = admin;
this.dataSource = new MatTableDataSource<Admin>(this.admins);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
});
}
filter(event: Event) {
const filtro = (event.target as HTMLInputElement).value;
this.dataSource.filter = filtro.trim().toLowerCase();
}
}
Console Log of admin subscribe:
{admins: [
...
{
"code": "002",
"name": "Manuel",
"pwd": "...",
"fsbs": false,
"createdIp": "157.100.75.247",
"createdAt": "2022-09-07T23:08:08.310Z",
"updatedIp": "157.100.75.247",
"updatedAt": "2022-09-07T23:08:08.310Z",
"logo": "..."
},
...
]
}