$ npm ci
$ npx nx serve ou-ui
This simple project was created as a PoC to show comparison of two different approaches to handling API calls using NgRx Store and @nrwl/angular.
check these files for more info:
Using loading progress in views triggered by loading flags on FE.
This pattern is relying on 3 types of NgRx Store Actions:
- Trigger Action (create, update/edit, delete) which triggers API call; e.g.
deleteTask
- Success Action which processes the response from API; e.g.
deleteTaskSuccess
- Failure Action which processes the error response from API; e.g.
deleteTaskFailure
Classic success | Classic error |
---|---|
import * as TasksActions from './tasks.actions';
...
deleteTask$ = createEffect(() =>
this.actions$.pipe(
ofType(TasksActions.deleteTask),
fetch({
run: ({ id }) =>
this.fakeAPI
.deleteTask(id)
.pipe(map((id) => TasksActions.deleteTaskSuccess({ id }))),
onError: (action, error) => {
console.error('Error deleteTask$: ', error);
this.message.error('Could not delete task');
return TasksActions.deleteTaskFailure({ error });
},
})
)
);
Classic success | Classic error |
---|---|
import * as TasksActions from './tasks.actions';
...
createTask$ = createEffect(() =>
this.actions$.pipe(
ofType(TasksActions.createTask),
fetch({
run: (action) =>
this.fakeAPI
.createTask(action.name, action.status)
.pipe(map((task) => TasksActions.createTaskSuccess({ task }))),
onError: (action, error) => {
console.error('Error createTask$: ', error);
this.message.error('Could not create task');
return TasksActions.createTaskFailure({ error });
},
})
)
);
Classic success | Classic error |
---|---|
The Optimistic UI is ideal when the responses from API take longer than desired from UX point of view. This approach assumes a high success rate of received API responses and (in ideal case*) expecting the response from API wouldn't differ from the actual data modified on client.
Most times is relying on 2 types of NgRx Store Actions:
- Trigger Action (create, update/edit, delete) which triggers API call AND immediately sets the new state containing new data on client (in store); e.g.
deleteTaskOptimistic
- Undo Action which is triggered upon receiving an error from API. Its purpose is to revert the new state to original state before the data modification.
Optimistic success | Optimistic error |
---|---|
import * as TasksActions from './tasks.actions';
...
deleteTaskOptimistic$ = createEffect(() =>
this.actions$.pipe(
ofType(TasksActions.deleteTaskOptimistic),
optimisticUpdate({
run: ({ task }) =>
this.fakeAPI.deleteTask(task.id).pipe(switchMap(() => EMPTY)),
undoAction: ({ task }, error) => {
console.error('Error deleteTask$: ', error);
this.message.error('Could not delete task');
return TasksActions.undoDeleteTask({
error,
task,
});
},
})
)
);
Optimistic success | Optimistic error |
---|---|
In some cases, just as in optimistic create, there can be a requirement for optimistic approach even though the API response differs in some way from the data modified on client. This use-case can be handled by adding a Success Action which re-maps or adds missing data to our entity.
Optimistic success | Optimistic error |
---|---|
In this case we don't know what ID will be generated on BE, so we need to generate our own on client and replace it in Success Action
import { optimisticUpdate } from '@nrwl/angular';
import * as TasksActions from './tasks.actions';
...
createTaskOptimistic$ = createEffect(() =>
this.actions$.pipe(
ofType(TasksActions.createTaskOptimistic),
optimisticUpdate({
run: (action) =>
this.api.createTask(action.task.name, action.task.status).pipe(
tap(() => this.message.success('Task created')),
// needs another action for replacing Optimistic ID (oid)
map((task) =>
TasksActions.createTaskOptimisticSuccess({
oid: action.task.id,
task,
})
)
),
undoAction: (action, error) => {
// add error handling here
console.error('Error createTask$');
// call Undo Action
return TasksActions.undoCreateTask({
error,
/**
* notice the the Undo Action's payload - in this case ID is enough,
* we're just removing it from the TasksEntities
**/
id: action.task.id,
});
},
})
)
);
Optimistic success | Optimistic error |
---|---|