Use store for formations feature
This commit is contained in:
parent
527f8592c5
commit
f7e0ac0626
|
@ -1,5 +1,9 @@
|
|||
import {Routes} from '@angular/router';
|
||||
import {AppAuthGuard} from "./auth/auth.guard";
|
||||
import {provideState} from "@ngrx/store";
|
||||
import {featureStateName as formationsFeature, formationsReducers} from "./formations/store";
|
||||
import {provideEffects} from "@ngrx/effects";
|
||||
import {FormationsEffects} from "./formations/store/formations.effects";
|
||||
|
||||
export const ROOT_ROUTES: Routes = [
|
||||
{
|
||||
|
@ -46,7 +50,11 @@ export const ROOT_ROUTES: Routes = [
|
|||
{
|
||||
path: 'formations',
|
||||
loadComponent: () => import("./formations/formations.component").then(mod => mod.FormationsComponent),
|
||||
canActivate: [AppAuthGuard]
|
||||
canActivate: [AppAuthGuard],
|
||||
providers: [
|
||||
provideState(formationsFeature, formationsReducers),
|
||||
provideEffects([FormationsEffects])
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
<ion-label i18n class="bold ion-text-end">Length</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-sliding>
|
||||
<ion-item *ngFor="let formation of formations">
|
||||
<ion-item *ngFor="let formation of formations$ | async">
|
||||
<ion-label>{{ formation.name }}</ion-label>
|
||||
<ion-label>{{ formation.formation }}</ion-label>
|
||||
<ion-label class="ion-text-end">{{ formation.length }}</ion-label>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {Component, inject, OnInit} from '@angular/core';
|
||||
import {
|
||||
IonButton,
|
||||
IonButtons,
|
||||
|
@ -32,7 +32,6 @@ import {
|
|||
addSharp,
|
||||
mapOutline,
|
||||
mapSharp,
|
||||
time,
|
||||
timeOutline,
|
||||
timeSharp,
|
||||
trainOutline,
|
||||
|
@ -41,8 +40,10 @@ import {
|
|||
trashSharp
|
||||
} from "ionicons/icons";
|
||||
import {Route} from "../routes/model/route";
|
||||
import {Formation} from "../formations/model/formation";
|
||||
import {Timetable} from ".././timetables/model/timetable";
|
||||
import {allFormations, FormationsState} from "../formations/store";
|
||||
import {Store} from "@ngrx/store";
|
||||
import {loadAllFormationsAction} from "../formations/store/formations.actions";
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
|
@ -77,7 +78,8 @@ import {Timetable} from ".././timetables/model/timetable";
|
|||
IonFooter
|
||||
]
|
||||
})
|
||||
export class DashboardComponent {
|
||||
export class DashboardComponent implements OnInit {
|
||||
private readonly formationsStore: Store<FormationsState> = inject(Store<FormationsState>);
|
||||
loggedIn$: Observable<boolean>;
|
||||
routes: Route[] = [
|
||||
{
|
||||
|
@ -89,7 +91,7 @@ export class DashboardComponent {
|
|||
}
|
||||
];
|
||||
timetables: Timetable[] = [];
|
||||
formations: Formation[] = [];
|
||||
formations$ = this.formationsStore.select(allFormations());
|
||||
|
||||
constructor(private readonly keycloakService: KeycloakService) {
|
||||
this.loggedIn$ = from(this.keycloakService.isLoggedIn());
|
||||
|
@ -108,5 +110,7 @@ export class DashboardComponent {
|
|||
});
|
||||
}
|
||||
|
||||
protected readonly time = time;
|
||||
ngOnInit() {
|
||||
this.formationsStore.dispatch(loadAllFormationsAction());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<ion-label i18n class="bold ion-text-end">Length</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-sliding>
|
||||
<ion-item *ngFor="let formation of formations">
|
||||
<ion-item *ngFor="let formation of formations$ | async">
|
||||
<ion-label>{{ formation.name }}</ion-label>
|
||||
<ion-label>{{ formation.formation }}</ion-label>
|
||||
<ion-label class="ion-text-end">{{ formation.length }}</ion-label>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Component} from '@angular/core';
|
||||
import {Component, inject, OnInit} from '@angular/core';
|
||||
import {
|
||||
IonButtons,
|
||||
IonContent,
|
||||
|
@ -20,8 +20,10 @@ import {
|
|||
} from "@ionic/angular/standalone";
|
||||
import {addIcons} from "ionicons";
|
||||
import {addOutline, addSharp, trashOutline, trashSharp} from "ionicons/icons";
|
||||
import {Formation} from "./model/formation";
|
||||
import {NgForOf} from "@angular/common";
|
||||
import {AsyncPipe, NgForOf} from "@angular/common";
|
||||
import {allFormations, FormationsState} from "./store";
|
||||
import {Store} from "@ngrx/store";
|
||||
import {loadAllFormationsAction} from "./store/formations.actions";
|
||||
|
||||
@Component({
|
||||
selector: 'app-formations',
|
||||
|
@ -46,12 +48,13 @@ import {NgForOf} from "@angular/common";
|
|||
IonFab,
|
||||
IonFabButton,
|
||||
IonFooter,
|
||||
NgForOf
|
||||
NgForOf,
|
||||
AsyncPipe
|
||||
]
|
||||
})
|
||||
export class FormationsComponent {
|
||||
|
||||
formations: Formation[] = [];
|
||||
export class FormationsComponent implements OnInit {
|
||||
private readonly store: Store<FormationsState> = inject(Store<FormationsState>);
|
||||
formations$ = this.store.select(allFormations());
|
||||
|
||||
constructor() {
|
||||
addIcons({
|
||||
|
@ -62,4 +65,8 @@ export class FormationsComponent {
|
|||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.dispatch(loadAllFormationsAction());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
import {FormationsService} from './formations.service';
|
||||
|
||||
describe('FormationsService', () => {
|
||||
let service: FormationsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(FormationsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {Formation} from "./model/formation";
|
||||
import {catchError, Observable, of} from "rxjs";
|
||||
import {HttpClient, HttpHeaders} from "@angular/common/http";
|
||||
import {environment} from "../../environments/environment";
|
||||
import {MessageType} from "../messages/model/message-type";
|
||||
import {MessagesService} from "../messages/messages.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FormationsService {
|
||||
|
||||
httpOptions = {
|
||||
headers: new HttpHeaders({'Content-Type': 'application/json'})
|
||||
};
|
||||
private formationsURL = environment.backendURL + '/formation';
|
||||
|
||||
constructor(private readonly http: HttpClient,
|
||||
private readonly messageService: MessagesService) {
|
||||
}
|
||||
|
||||
fetchFormations(): Observable<Formation[]> {
|
||||
return this.http.get<Formation[]>(this.formationsURL, this.httpOptions)
|
||||
.pipe(
|
||||
catchError(this.handleError<Formation[]>('fetchFormations', []))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Http operation that failed.
|
||||
* Let the app continue.
|
||||
* @param operation - name of the operation that failed
|
||||
* @param result - optional value to return as the observable result
|
||||
*/
|
||||
private handleError<T>(operation = 'operation', result?: T) {
|
||||
return (error: any): Observable<T> => {
|
||||
|
||||
// TODO: send the error to remote logging infrastructure
|
||||
console.error(error); // log to console instead
|
||||
|
||||
if (error.status == 0) {
|
||||
this.log(MessageType.UNKNOWN_ERROR);
|
||||
}
|
||||
if (error.status == 401) {
|
||||
this.log(MessageType.UNAUTHENTICATED);
|
||||
}
|
||||
if (error.status == 403) {
|
||||
this.log(MessageType.UNAUTHORIZED);
|
||||
}
|
||||
if (error.status == 500) {
|
||||
this.log(MessageType.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
// Let the app keep running by returning an empty result.
|
||||
return of(result as T);
|
||||
};
|
||||
}
|
||||
|
||||
private log(type: MessageType) {
|
||||
this.messageService.logMessage('FormationsService', type);
|
||||
}
|
||||
}
|
|
@ -3,3 +3,10 @@ export interface Formation {
|
|||
formation: string;
|
||||
length: number;
|
||||
}
|
||||
|
||||
|
||||
export const DEFAULT_FORMATION: Formation = {
|
||||
name: "",
|
||||
formation: "",
|
||||
length: 0,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import {createAction, props} from "@ngrx/store";
|
||||
import {Formation} from "../model/formation";
|
||||
|
||||
export enum ActionTypes {
|
||||
LoadAllFormations = '[Formations] Load All Formations',
|
||||
LoadAllFormationsFinished = '[Formations] Load All Formations Finished',
|
||||
|
||||
LoadSingleFormation = '[Formations] Load Single Formation',
|
||||
LoadSingleFormationFinished = '[Formations] Load Single Formation Finished',
|
||||
|
||||
AddFormation = '[Formations] Add Formation',
|
||||
UpdateFormation = '[Formations] Update Formation',
|
||||
DeleteFormation = '[Formations] Delete Formation',
|
||||
}
|
||||
|
||||
export const loadAllFormationsAction = createAction(
|
||||
ActionTypes.LoadAllFormations
|
||||
);
|
||||
|
||||
export const loadAllFormationsFinishedAction = createAction(
|
||||
ActionTypes.LoadAllFormationsFinished,
|
||||
props<{ payload: Formation[] }>()
|
||||
);
|
||||
|
||||
export const loadSingleFormationAction = createAction(
|
||||
ActionTypes.LoadSingleFormation,
|
||||
props<{ payload: string }>()
|
||||
);
|
||||
|
||||
export const loadSingleFormationFinishedAction = createAction(
|
||||
ActionTypes.LoadSingleFormationFinished,
|
||||
props<{ payload: Formation }>()
|
||||
);
|
||||
|
||||
export const addFormationAction = createAction(
|
||||
ActionTypes.AddFormation,
|
||||
props<{ payload: Formation }>()
|
||||
);
|
||||
|
||||
export const updateFormationAction = createAction(
|
||||
ActionTypes.UpdateFormation,
|
||||
props<{ payload: Formation }>()
|
||||
);
|
||||
|
||||
export const deleteFormation = createAction(
|
||||
ActionTypes.DeleteFormation,
|
||||
props<{ payload: Formation }>()
|
||||
);
|
|
@ -0,0 +1,18 @@
|
|||
import {inject, Injectable} from "@angular/core";
|
||||
import {Actions, createEffect, ofType} from "@ngrx/effects";
|
||||
import {loadAllFormationsAction, loadAllFormationsFinishedAction} from "./formations.actions";
|
||||
import {map, switchMap} from "rxjs";
|
||||
import {FormationsService} from "../formations.service";
|
||||
|
||||
@Injectable()
|
||||
export class FormationsEffects {
|
||||
loadAllFormations$ = createEffect(
|
||||
(actions$ = inject(Actions), formationsService = inject(FormationsService)) => {
|
||||
return actions$.pipe(
|
||||
ofType(loadAllFormationsAction),
|
||||
switchMap(() => formationsService.fetchFormations()),
|
||||
map(formations => loadAllFormationsFinishedAction({payload: formations}))
|
||||
)
|
||||
},
|
||||
{functional: true});
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import {DEFAULT_FORMATION, Formation} from "../model/formation";
|
||||
import {createReducer, on} from "@ngrx/store";
|
||||
import {loadAllFormationsFinishedAction, loadSingleFormationFinishedAction} from "./formations.actions";
|
||||
|
||||
export interface ReducerFormationsState {
|
||||
items: Formation[];
|
||||
selectedItem: Formation;
|
||||
}
|
||||
|
||||
export const initialState: ReducerFormationsState = {
|
||||
items: [],
|
||||
selectedItem: DEFAULT_FORMATION,
|
||||
};
|
||||
|
||||
export const formationsReducer = createReducer(
|
||||
initialState,
|
||||
on(loadAllFormationsFinishedAction, (state,
|
||||
action) => ({
|
||||
...state,
|
||||
items: [...action.payload]
|
||||
})),
|
||||
on(loadSingleFormationFinishedAction, (state,
|
||||
action) => ({
|
||||
...state,
|
||||
selectedItem: action.payload
|
||||
})),
|
||||
);
|
|
@ -0,0 +1,28 @@
|
|||
import {formationsReducer, ReducerFormationsState} from "./formations.reducer";
|
||||
import {ActionReducerMap, createFeatureSelector, createSelector} from "@ngrx/store";
|
||||
|
||||
export const featureStateName = 'formationsFeature';
|
||||
|
||||
export interface FormationsState {
|
||||
formations: ReducerFormationsState;
|
||||
}
|
||||
|
||||
export const formationsReducers: ActionReducerMap<FormationsState> = {
|
||||
formations: formationsReducer,
|
||||
};
|
||||
|
||||
// extract the main property 'formationsFeature' from the state object
|
||||
export const getFormationsFeatureState = createFeatureSelector<FormationsState>(
|
||||
featureStateName
|
||||
);
|
||||
|
||||
export const allFormations = () => createSelector(
|
||||
getFormationsFeatureState,
|
||||
(state: FormationsState) => state.formations.items
|
||||
);
|
||||
|
||||
export const selectedFormation = () => createSelector(
|
||||
getFormationsFeatureState,
|
||||
(state: FormationsState) =>
|
||||
state.formations.selectedItem
|
||||
);
|
Loading…
Reference in New Issue