feat(doc): move effect-selector

This commit is contained in:
thomas
2023-10-18 09:51:22 +02:00
parent a2d6f2471d
commit 27e7137716
37 changed files with 34 additions and 29 deletions

View File

@@ -0,0 +1,14 @@
import { createAction, props } from '@ngrx/store';
import { Activity } from './activity.model';
export const loadActivities = createAction('[AppComponent] Load Activities');
export const loadActivitiesSuccess = createAction(
'[Activity Effect] Load Activitys Success',
props<{ activities: Activity[] }>()
);
export const loadActivitiesFailure = createAction(
'[Activity Effect] Load Activities Failure',
props<{ error: any }>()
);

View File

@@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, concatMap, map } from 'rxjs/operators';
import * as ActivityActions from './activity.actions';
import { ActivityService } from './activity.service';
@Injectable()
export class ActivityEffects {
loadActivities$ = createEffect(() => {
return this.actions$.pipe(
ofType(ActivityActions.loadActivities),
concatMap(() =>
this.ActivityService.fetchActivities().pipe(
map((activities) =>
ActivityActions.loadActivitiesSuccess({ activities })
),
catchError((error) =>
of(ActivityActions.loadActivitiesFailure({ error }))
)
)
)
);
});
constructor(
private actions$: Actions,
private ActivityService: ActivityService
) {}
}

View File

@@ -0,0 +1,55 @@
import {
incrementalNumber,
rand,
randFirstName,
randText,
} from '@ngneat/falso';
export const activityType = [
'Sport',
'Sciences',
'History',
'Maths',
'Physics',
] as const;
export type ActivityType = (typeof activityType)[number];
export interface Person {
id: number;
name: string;
}
export interface Activity {
id: number;
name: string;
type: ActivityType;
teacher: Person;
}
const factoryPerson = incrementalNumber();
export const randPerson = () => ({
id: factoryPerson(),
name: randFirstName(),
});
const factoryActivity = incrementalNumber();
export const randActivity = (): Activity => ({
id: factoryActivity(),
name: randText(),
type: rand(activityType),
teacher: randPerson(),
});
export const activities: Activity[] = [
randActivity(),
randActivity(),
randActivity(),
randActivity(),
randActivity(),
randActivity(),
randActivity(),
randActivity(),
randActivity(),
];

View File

@@ -0,0 +1,25 @@
import { createReducer, on } from '@ngrx/store';
import * as ActivityActions from './activity.actions';
import { Activity } from './activity.model';
export const activityFeatureKey = 'Activity';
export interface ActivityState {
activities: Activity[];
}
export const initialState: ActivityState = {
activities: [],
};
export const activityReducer = createReducer(
initialState,
on(ActivityActions.loadActivitiesSuccess, (state, { activities }) => ({
...state,
activities,
})),
on(ActivityActions.loadActivitiesFailure, (state) => ({
state,
activities: [],
}))
);

View File

@@ -0,0 +1,10 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { activityFeatureKey, ActivityState } from './activity.reducer';
export const selectActivityState =
createFeatureSelector<ActivityState>(activityFeatureKey);
export const selectActivities = createSelector(
selectActivityState,
(state) => state.activities
);

View File

@@ -0,0 +1,10 @@
import { Injectable } from '@angular/core';
import { map, timer } from 'rxjs';
import { activities } from './activity.model';
@Injectable({
providedIn: 'root',
})
export class ActivityService {
fetchActivities = () => timer(500).pipe(map(() => activities));
}

View File

@@ -0,0 +1,9 @@
import { createAction, props } from '@ngrx/store';
import { Status } from './status.model';
export const loadStatuses = createAction('[Status] Load Statuses');
export const loadStatusesSuccess = createAction(
'[Status] Load Statuses Success',
props<{ statuses: Status[] }>()
);

View File

@@ -0,0 +1,49 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { combineLatest, concatMap, map } from 'rxjs';
import { selectActivities } from '../activity/activity.selectors';
import { selectUser } from '../user/user.selectors';
import * as StatusActions from './status.actions';
import { Status } from './status.model';
@Injectable()
export class StatusEffects {
loadStatuses$ = createEffect(() => {
return this.actions$.pipe(
ofType(StatusActions.loadStatuses),
concatMap(() =>
combineLatest([
this.store.select(selectUser),
this.store.select(selectActivities),
]).pipe(
map(([user, activities]): Status[] => {
if (user?.isAdmin) {
return activities.reduce(
(status: Status[], activity): Status[] => {
const index = status.findIndex(
(s) => s.name === activity.type
);
if (index === -1) {
return [
...status,
{ name: activity.type, teachers: [activity.teacher] },
];
} else {
status[index].teachers.push(activity.teacher);
return status;
}
},
[]
);
}
return [];
}),
map((statuses) => StatusActions.loadStatusesSuccess({ statuses }))
)
)
);
});
constructor(private actions$: Actions, private store: Store) {}
}

View File

@@ -0,0 +1,6 @@
import { ActivityType, Person } from '../activity/activity.model';
export interface Status {
name: ActivityType;
teachers: Person[];
}

View File

@@ -0,0 +1,29 @@
import { createReducer, on } from '@ngrx/store';
import { ActivityType, Person } from '../activity/activity.model';
import * as StatusActions from './status.actions';
import { Status } from './status.model';
export const statusFeatureKey = 'status';
export interface StatusState {
statuses: Status[];
teachersMap: Map<ActivityType, Person[]>;
}
export const initialState: StatusState = {
statuses: [],
teachersMap: new Map(),
};
export const statusReducer = createReducer(
initialState,
on(StatusActions.loadStatusesSuccess, (state, { statuses }): StatusState => {
const map = new Map();
statuses.forEach((s) => map.set(s.name, s.teachers));
return {
...state,
statuses,
teachersMap: map,
};
})
);

View File

@@ -0,0 +1,17 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { ActivityType } from '../activity/activity.model';
import { statusFeatureKey, StatusState } from './status.reducer';
export const selectStatusState =
createFeatureSelector<StatusState>(statusFeatureKey);
export const selectStatuses = createSelector(
selectStatusState,
(state) => state.statuses
);
export const selectAllTeachersByActivityType = (name: ActivityType) =>
createSelector(
selectStatusState,
(state) => state.teachersMap.get(name) ?? []
);

View File

@@ -0,0 +1,14 @@
import { createAction, props } from '@ngrx/store';
import { User } from './user.model';
export const loadUsers = createAction('[User] Load User');
export const loadUsersSuccess = createAction(
'[User] Load Users Success',
props<{ user: User }>()
);
export const loadUsersFailure = createAction(
'[User] Load Users Failure',
props<{ error: any }>()
);

View File

@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, concatMap, map } from 'rxjs/operators';
import * as UserActions from './user.actions';
import { UserService } from './user.service';
@Injectable()
export class UserEffects {
loadUsers$ = createEffect(() => {
return this.actions$.pipe(
ofType(UserActions.loadUsers),
concatMap(() =>
this.userService.fetchUser().pipe(
map((user) => UserActions.loadUsersSuccess({ user })),
catchError((error) => of(UserActions.loadUsersFailure({ error })))
)
)
);
});
constructor(private actions$: Actions, private userService: UserService) {}
}

View File

@@ -0,0 +1,15 @@
import { randFirstName, randLastName, randText } from '@ngneat/falso';
export interface User {
firstname: string;
lastname: string;
isAdmin: boolean;
apiKey: string;
}
export const user: User = {
firstname: randFirstName(),
lastname: randLastName(),
isAdmin: true,
apiKey: randText(),
};

View File

@@ -0,0 +1,19 @@
import { createReducer, on } from '@ngrx/store';
import * as UserActions from './user.actions';
import { User } from './user.model';
export const userFeatureKey = 'user';
export interface UserState {
user?: User;
}
export const initialState: UserState = {
user: undefined,
};
export const userReducer = createReducer(
initialState,
on(UserActions.loadUsersSuccess, (state, { user }) => ({ ...state, user })),
on(UserActions.loadUsersFailure, (state) => ({ ...state, user: undefined }))
);

View File

@@ -0,0 +1,9 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { userFeatureKey, UserState } from './user.reducer';
export const selectUserState = createFeatureSelector<UserState>(userFeatureKey);
export const selectUser = createSelector(
selectUserState,
(state) => state.user
);

View File

@@ -0,0 +1,10 @@
import { Injectable } from '@angular/core';
import { map, timer } from 'rxjs';
import { user } from './user.model';
@Injectable({
providedIn: 'root',
})
export class UserService {
fetchUser = () => timer(500).pipe(map(() => user));
}