mirror of
https://github.com/Raghu-Ch/angular-challenges.git
synced 2026-02-13 14:23:02 -05:00
feat(doc): move effect-selector
This commit is contained in:
@@ -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 }>()
|
||||
);
|
||||
@@ -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
|
||||
) {}
|
||||
}
|
||||
@@ -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(),
|
||||
];
|
||||
@@ -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: [],
|
||||
}))
|
||||
);
|
||||
@@ -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
|
||||
);
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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[] }>()
|
||||
);
|
||||
@@ -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) {}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { ActivityType, Person } from '../activity/activity.model';
|
||||
|
||||
export interface Status {
|
||||
name: ActivityType;
|
||||
teachers: Person[];
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
})
|
||||
);
|
||||
@@ -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) ?? []
|
||||
);
|
||||
14
apps/ngrx/effect-selector/src/app/store/user/user.actions.ts
Normal file
14
apps/ngrx/effect-selector/src/app/store/user/user.actions.ts
Normal 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 }>()
|
||||
);
|
||||
23
apps/ngrx/effect-selector/src/app/store/user/user.effects.ts
Normal file
23
apps/ngrx/effect-selector/src/app/store/user/user.effects.ts
Normal 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) {}
|
||||
}
|
||||
15
apps/ngrx/effect-selector/src/app/store/user/user.model.ts
Normal file
15
apps/ngrx/effect-selector/src/app/store/user/user.model.ts
Normal 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(),
|
||||
};
|
||||
19
apps/ngrx/effect-selector/src/app/store/user/user.reducer.ts
Normal file
19
apps/ngrx/effect-selector/src/app/store/user/user.reducer.ts
Normal 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 }))
|
||||
);
|
||||
@@ -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
|
||||
);
|
||||
10
apps/ngrx/effect-selector/src/app/store/user/user.service.ts
Normal file
10
apps/ngrx/effect-selector/src/app/store/user/user.service.ts
Normal 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));
|
||||
}
|
||||
Reference in New Issue
Block a user