2. Objectif
L’objectif de ce workshop est de créer une application web TODO List via laquelle
nous allons explorer les features d’Angular2.
Le résultat finale de l’application:
Nb : Pour les commandes et le code à intégrer, il faut juste un simple copier depuis
le tp coller vers votre console ou projet.
1. Création d’un projet ANGULAR2
Une fois vous avez bien installé NodeJs & Npm, procédez comme suit pour créer
votre premier projet Angular2
1. Créer un dossier dans lequel vous voulez enregistrer votre travail nommé
ProjetAngular
2. Ouvrir l’invite de commande.
3. Accéder au dossier ProjetAngular
4. Taper
a/ ng new todo-app
b/ cd todo-app
c/ ng serve --open
Une fenêtre de votre navigateur s’ouvre avec l’url localhost:4200
2
3. 2. Comprendre la structure du projet
Package.json: contient les packages nécessaires à la compilation et exécution de
l’application.
Node_modules: contient l’installation de tous les packages qui se trouvent dans le
fichier package.json ainsi que d’autres packages par défaut.
e2e: ( end to end tests) contient les tests de l’application et ses configurations.
.angular-cli.json: contient les configurations d’Angular-CLI.
.editorconfig: contient les configurations de l’éditeur utilisé.
.gitignore: contient les fichiers/dossiers qui vont être ignorés de contrôle lors d’un
“commit” via git.
karma.conf.js: contient les configurations de Karma( Unit Test Runner)
protractor.conf.js: contient les configurations de Protractor( E2E test Framework)
README.md: contient la documentation de l’application.
tsconfig.json: contient les configurations de compilateur Typescript.
src: contient le code source du projet.
3
4. src/assets : contient les images et toute ressources de l’application.
src/main.ts: le point d’entrée de l’application. C’est la page depuis laquelle
l’application sera chargée. (bootstrapping, loading)
src/app.module.ts:
src/app : Contient les fichiers app.component( .ts, .html, .css, .spec) relatives au
component , template, stylesheet et test unitaire.
src/index.html: c’est la page qui va charger toutes les autres pages pour assurer le
principe de SPA’(Single Page Application).
src/test.ts: le point d’entré pour les tests unitaires.
3. Création d’un component
Angular-CLI offre une directive pour créer les components automatiquement.
Nous allons créer component nommé Todo.
Ouvrir un nouveau console/terminal et accéder au répertoire de votre projet. puis
taper
ng g component todo
Vous allez trouver un nouveau dossier ajouté
Explorer les nouveaux fichiers!
Remplacez le contenu de fichier src/app/app.component.html par ce code :
<app-todo></app-todo>
Nb: Une fois vous sauvegardez la modification, et si vous avez ng serve déjà
activé, alors il vous doit afficher automatiquement le message : “todo works!”.
4. TODO Template
Nous allons changer le contenu de fichier src/app/todo/todo.component.html
pour afficher une liste TODO au lieu l’affichage du texte “todo works!” :
4
5. <section class="todoapp">
<header class="header">
<h1>Todo</h1>
<input class="new-todo" placeholder="What needs
to be done?" autofocus>
</header>
<!-- This section should be hidden by default and
shown when there are todos -->
<section class="main">
<ul class="todo-list">
<!-- These are here just to show the
structure of the list items -->
<!-- List items should get the class
`editing` when editing and `completed` when marked
as completed -->
<li class="completed">
<div class="view">
<input class="toggle" type="checkbox"
checked>
<label>Install angular-cli</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC
template">
</li>
<li>
<div class="view">
<input class="toggle" type="checkbox">
<label>Understand Angular2 apps</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Rule the
web">
</li>
</ul>
</section>
<!-- This footer should hidden by default and
shown when there are todos -->
<footer class="footer">
<!-- This should be `0 items left` by
default -->
<span class="todo-count"><strong>0</strong>
item left</span>
<!-- Remove this if you don't implement
routing -->
<ul class="filters">
<li>
<a class="selected" href="#/">All</a>
</li>
<li>
<a href="#/active">Active</a>
</li>
<li>
<a href="#/completed">Completed</a>
</li>
</ul>
<!-- Hidden if no completed items are left
↓ -->
<button class="clear-completed">Clear
completed</button>
</footer>
</section>
5
6. L’affichage sera comme suit :
5. Ajout du style à l’application
Pour changer le style de l’affichage, il y en a plusieurs méthodes d’en la plus simple
de d’utiliser un style prédéfini.
npm install --save todomvc-app-css
Puis dans le tableau style dans le fichier angular-cli.json copier ce code : (il
faut juste remplacer le code du tableau styles)
"styles": [
"../node_modules/todomvc-app-css/index.css"],Afin de voir les
modifications , stopper ng serve par ctrl+c puis la relancer de nouveau, l’affichage
6
7. sera comme suit:
Maintenant, nous allons travailler sur les actions ‘ajout/modification/suppression’
d’un todo.
6. TODO service
Angular CLI offre une commande pour créer un service pour la manipulation des
données :
ng g service todo/todo
Ça va nous créer 2 fichiers : src/app/todos/todo.service.spec.ts et
src/app/todos/todo.service.ts
7. CRUD
7.1. READ: Afficher tous les TODOs
Nous allons modifier le code de todo.service.ts comme suit:
*/ Ajouter dans la ligne 2 le tableau todos :
let todos = [
{ title: 'Install Angular CLI', isDone: true, _id:0 },
{ title: 'Style app', isDone: true, _id:1 },
{title:'Finish service',isDone: false , _id:2},
{ title: 'Setup API', isDone: false , _id:3 } ];
*/ Ajouter le service get dans la classe TodoService
get(){return new Promise(resolve => resolve(todos)); }
Maintenant, il faut faire des changements dans le fichier todo.component.ts
pour qu’il utilise le service déjà crée.
7
8. */ import { TodoService } from './todo.service';
*/Dans la clause @component, ajouter cette directive :
providers: [TodoService]
*/ Modifier le code de la classe TodoComponent en y plaçant ce code:
private todos;
private activeTasks;
constructor(private todoService: TodoService) { }
getTodos(){
return this.todoService.get().then(todos => {
this.todos = todos;
this.activeTasks = this.todos.filter(todo =>
todo.isDone).length;
}); }
ngOnInit() {
this.getTodos(); }
Rien n’a changé, c’est simplement parce que nous n’avons pas changé le template
( todo.component.html). Il faut le changer comme suit:
changer le contenu du premier <ul></ul> par :
<li *ngFor="let todo of todos" [ngClass]="{completed:
todo.isDone}" >
<div class="view">
<input class="toggle" type="checkbox"
[checked]="todo.isDone">
<label>{{todo.title}}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="{{todo.title}}">
</li>
8
9. Ensuite, changer cette ligne :
<<span class="todo-count"><strong>0</strong> item
left</span>
Par:
<<span class="todo-count"><strong>{{activeTasks}}</strong>
item left</span>
LL’affichage sera comme suit:
7.2. CREATE todo en utilisant input:
Changer l’input qui se trouve dans la section Header du fichier
todo.component.html par ce code:
<input class="new-todo"
placeholder="What needs to be done?"
[(ngModel)]="newTodo"
(keyup.enter)="addTodo()" autofocus>
Nb: ngModel n’est pas généré automatiquement, vous devez apporter quelques
modifications dans le fichier app.module.ts par :
1/ Importer le module FormsModule
9
10. import {FormsModule} from '@angular/forms';
*/ Ajouter FormsModule au tableau ‘imports’
imports :[ ... , FormsModule ]
Puis dans le fichier todo.component.ts placez ce code pour définir ‘addTodo’
et newTodo:
private newTodo;
addTodo(){
this.todoService.add({ title: this.newTodo, isDone: false,
_id: this.todos.length +1 }).then(() => {
return this.getTodos();
}).then(() => {
this.newTodo = ''; // clear input form value
}); }
Nous allons maintenant définir le service ‘this.todoService.add’ dans le fichier
todo.service.ts
add(data) {
return new Promise(resolve => {
todos.push(data);
resolve(data); }); }
Tester donc l’ajout !
7.2.1 Marquer TODO Item comme completed
Lors du click sur le bouton (check button) nous voulons changer le statut du todo
Item en completed. Pour cela , suivez ces étapes:
Dans todo.component.html ,ajouter le l’évenement (click) sur le bouton de
type ckeckbox comme suit:
<input class="toggle" type="checkbox" [checked]="todo.isDone"
(click)="updateTodoisDone(todo, todo.isDone)" >
Définir la fonction updateTodoIsDone dans le fichier todo.component.ts
10
11. updateTodoisDone(todo, bool) {
bool = !bool;
todo.isDone = bool;
return this.todoService.put(todo).then(() => {
todo.editing = false;
return this.getTodos(); }); }
Nous allons maintenant définir le service ‘this.todoService.put’ dans le fichier
todo.service.ts
put(data) {
return new Promise(resolve => {
let index = todos.findIndex(todo => todo._id ===
data._id);
todos[index].title = data.title;
resolve(data);
});
}
Tester donc le checkbox !
7.3. UPDATE en double click sur todo item :
La mise à jour d’un todo item se fait en y cliquant 2fois. Un nouveau input va
apparaitre , écrivez là-ici la nouvelle valeur et puis entrer.
Pour le faire, placez vous dans le fichier todo.component.html et changer le
code de <ul> </ul> comme suit:
<li *ngFor="let todo of todos" [ngClass]="{completed:
todo.isDone, editing: todo.editing}" >
<div class="view">
11
12. <input class="toggle" type="checkbox"
[checked]="todo.isDone" (click)="updateTodoisDone(todo,
todo.isDone)" >
<label (dblclick)="todo.editing =
true">{{todo.title}}</label>
<button class="destroy"></button>
</div>
<input class="edit"
#updatedTodo
[value]="todo.title"
(blur)="updateTodo(todo, updatedTodo.value)"
(keyup.escape)="todo.editing = false"
(keyup.enter)="updateTodo(todo, updatedTodo.value)">
</li>
Une nouvelle fonction ‘updateTodo’ a été ajoutée. Par la suite nous devons la définir
au niveau todo.component.ts
updateTodo(todo, newValue) {
todo.title = newValue;
return this.todoService.put(todo).then(() => {
todo.editing = false;
return this.getTodos(); });}
ps: La fonction put a été déjà déclarée dans notre service
Tester donc la mise à jour !
7.4. DELETE en cliquant sur le bouton X :
On ajoute un ‘event listner’ sur le destroy (ligne 18):
<button class="destroy"(click)="destroyTodo(todo)"></button
Puis, on définie la fonction dans le fichier todo.component.ts comme suit :
12
13. destroyTodo(todo){
this.todoService.delete(todo._id).then(() => {
return this.getTodos();
}); }
Puis, on définie le service “delete” dans le fichier todo.service.ts comme
suit :
delete(id) {
return new Promise(resolve => {
let index = todos.findIndex(todo => todo._id === id);
todos.splice(index, 1);
resolve(true);
}); }
Tester donc la suppression !
8. ROUTING & NAVIGATION :
Vous avez 3 boutons dans l’interface :’all’, ‘active’, ‘completed’.
Nous voulons afficher les TODOs correspondant en cliquant sur ces boutons. Pour
cela nous allons utiliser une librairie nommée ‘Router’ qui va gérer le routing.
Dans le fichier app.module.ts , vous allez ajouter ce code:
import { Routes, RouterModule } from '@angular/router';
après les import déclarer ce tableau
const routes: Routes = [
{ path: ':status', component: TodoComponent },
{ path: '**', redirectTo: '/all' } ];
et puis au sein du @NgModule dans la directive imports ajouter ces
deux paramètres
imports: [..., RouterModule.forRoot(routes) ],
Puis, dans le fichier app.component.html remplacer <app-todo> </app-todo>
par:
<router-outlet></router-outlet>
Nb: l’URL de l’application devient par défaut: http://localhost:4200/all
8.1. Utilisation RouterLink & ActivatedRoute :
13
14. RouterLInk est égale exactement à ‘href’ pour les routes dynamiques qu’on peut le
remplacer par ‘/all’, ‘/completed’, ‘/active’.
*/ Modifier le code de class=’filters’ qui se trouve dans todo.component.html
par :
<li>
<a [routerLink]="['/all']" [class.selected]="path ===
'all'">All</a>
</li>
<li>
<a [routerLink]="['/active']" [class.selected]="path ===
'active'">Active</a>
</li>
<li> <a [routerLink]="['/completed']"
[class.selected]="path === 'completed'">Completed</a> </li>
*/ Dans le fichier todo.component.ts ajouter ce code :
import { ActivatedRoute } from '@angular/router';
Puis, créer une variable “path”
private path;
Changer le constructeur comme suit:
constructor(private todoService: TodoService, private
route: ActivatedRoute) { }
changer le code de la fonction ngOnInit() comme suit
ngOnInit() {
this.route.params.subscribe(params => {
this.path = params['status'];
this.getTodos(); }); }
Tester le click sur les boutons va changer l’URL ! Mais les données ne sont pas
encore filtrées! .
8.2. Filtrage des données selon les routes:
Pour filtrer la liste TODO selon active/completed, nous devons le passer en
paramètres dans todoservice.get . Pour cela nous allons modifier 2 fichiers:
*/ todo.component.ts
14
15. - Changer le code de ngOnInit() par:
ngOnInit() {
this.route.params.subscribe(params => {
this.path = params['status'];
this.getTodos(this.path); }); }
- Changer le code de getTodo() par:
getTodos(query = ''){
return this.todoService.get(query).then(todos => {
this.todos=todos;
this.activeTasks=this.todos.filter(todo=>
todo.isDone).length;});}
*/ todo.service.ts
Changer le code de service get() par:
get(query = ''){
return new Promise(resolve => {
var data;
if(query === 'completed' || query === 'active'){
var isCompleted = query === 'completed';
data = todos.filter(todo => todo.isDone ===
isCompleted);
} else {
data = todos;
}
resolve(data); }); }
Le contenu devient filtré , Testez-le !
9. Suppression des Completed TODO :
Dans le fichier todo.component.html,ajouter click sur le bouton
<<button class="clear-completed"
((click)="clearCompleted()">Clear completed</button>
15
16. Puis, dans le fichier todo.component.ts , définissez la fonction
clearCompleted():
clearCompleted() {
this.todoService.deleteCompleted().then(() => {
return this.getTodos(); }); }
Enfin, créer le service deleteCompleted dans le fichier todo.service.ts
deleteCompleted() {
return new Promise(resolve => {
todos = todos.filter(todo => !todo.isDone);
resolve(todos); });}
La fin :D
16