On a maintenant toutes nos fonctionnalités : Create, Select, Update, Delete. Cela reste du local ou du offline. Il est temps de mettre en place un serveur et d'y inclure des requêtes http.
Pour maximiser le temps, on va créer une API avec le module npm .
npm install -g json-serve
Dans notre projet, inclure un nouveau dossier /server au même niveau que /app et y ajouter un fichier json qui sera notre base de données :
src
|
└───app
|
└───server
|
└───db.json
db.json
{
"todos": []
}
Entrer dans le terminal :
json-server path-of-json
Allez sur localhost:3000/todos et vous avez une API prête à l'emploi.
Get Todos
Générer un service depuis le terminal :
ng g service services/todo-list
Déclarer dans le AppModule ainsi que le dernier module HttpClient d'Angular :
app.module.ts
// [...]
import { HttpClientModule } from '@angular/common/http';
import { TodoListService } from './services/todo-list.service';
@NgModule({
imports: [
// [...]
HttpClientModule
],
providers: [TodoListService]
})
export class TodoListModule { }
services/todo-list.service
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { Todo } from '@Models/todo';
import { environment } from '@Env';
@Injectable()
export class TodoListService {
constructor(private http:HttpClient) {}
getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(`${environment.apiUrl}/todos`);
}
}
Ajouter les trois actions pour l'effect : LOAD_INIT_TODOS, SUCCESS_INIT_TODOS, ERROR_INIT_TODOS.
Retirer l'action InitTodos
store/actions/todo-list.action.ts
export namespace TodoListModule {
export enum ActionTypes {
// [...]
LOAD_INIT_TODOS = '[todoList] Load Init Todos',
SUCCESS_INIT_TODOS = '[todoList] Success Init Todos',
ERROR_INIT_TODOS = '[todoList] Error Init Todos',
// a supprimer INIT_TODOS = '[todoList] Init Todos',
}
// [...]
/* A supprimer
export class InitTodos {
readonly type = ActionTypes.INIT_TODO;
constructor(public payload: Todo[]){ }
}
*/
export class LoadInitTodos {
readonly type = ActionTypes.LOAD_INIT_TODOS;
}
export class SuccessInitTodos {
readonly type = ActionTypes.SUCCESS_INIT_TODOS;
constructor(payload: todo[]){}
}
export class ErrorInitTodos {
readonly type = ActionTypes.ERROR_INIT_TODOS
}
// [...]
export type Actions = DeleteTodo
// | InitTodos
| LoadInitTodos
| SuccessInitTodos
| ErrorInitTodos;
}
// [...]
Nous avons deux étapes lors d'un LoadinitTodos, on peut donc faire un switch sur les propriétés dans le reducer et commencer à jouer avec les booléens loading & loaded .
Ce détail permettra de changer votre template lors du chargement des todos en ajoutant un loader et en désactivant les boutons durant la requête de chargement.
store/reducers/todo-list.reducer.ts
// [...]
export function todosReducer(
// [...]
case TodoListModule.ActionTypes.LOAD_INIT_TODOS:
// Passe le loading a true
return {
...state,
loading: true
};
case TodoListModule.ActionTypes.SUCCESS_INIT_TODOS:
// Bind state.data avec les todos du server
// Passe le loaded a true et le loading a false
return {
...state,
loading: false,
loaded: true,
data: action.payload
};
case TodoListModule.ActionTypes.ERROR_INIT_TODOS:
// Error rend le loading a false
return {
...state,
loading: false
};
/* A supprimer
case TodoListModule.ActionTypes.INIT_TODOS :
return {
...state,
data: [
...action.payload
]
};
*/
// [...]
Pour écrire notre premier Effect qui est un Observable, on peut utiliser tout ce que peut nous fournir RXJS pour faire du traitement, combiner des states etc... :
store/effects/todo-list.effect.ts
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@NGRX/effects';
import { Observable } from 'rxjs/Observable';
import { catchError, map, switchMap } from 'rxjs/operators';
import { TodoListModule } from '@Actions/todo-list.action';
import { TodoListService } from '../../services/todo-list';
@Injectable()
export class TodoListEffects {
// Ecoute les actions passées dans le store
@Effect() LoadTodos$: Observable<TodoListModule.Actions> = this.actions$
.pipe(
// Si l'action est de type 'LOAD_INIT_TODOS', applique la suite sinon ne fait rien
ofType(TodoListModule.ActionTypes.LOAD_INIT_TODOS),
// l'action du switchMap est l'objet d'action qui est récupérer dans le ofType
// action = { type: '[todoList] Load Init Todos' }
switchMap(action => this.todoListService.getTodos())
// Dans le switchMap, on éxécute le service qui retournera la réponse dans le map suivant
// todos = Todo[]
// Il n'y a plus qu'à renvoyer une action SuccessInitTodos avec les todos en params
map(todos => new TodoListModule.SuccessInitTodos(todos))
// Si le resolve n'a pas abouti, il passe dans la fonction catchError
// Qui renvoie l'action ErrorInitTodos
catchError(() => new TodoListModule.ErrorInitTodos())
);
constructor(
private todoListService: TodoListService,
private actions$: Actions
) {}
}
Pour finaliser l'implémentation des effects, déclarer un tableau d'effect dans l'index du store :
Plus besoin d'avoir le service dans le component. C'est maintenant l'effect qui, via le LoadInitTodos, va utiliser le service getTodos qui dispatchera notre liste de todos via l'action SuccessInitTodos.