Compare commits
4 commits
8e9bd1a6d0
...
17179b95f0
| Author | SHA1 | Date | |
|---|---|---|---|
| 17179b95f0 | |||
| 90d8e08859 | |||
| 883fcfb1a5 | |||
| fec717628c |
8 changed files with 2859 additions and 7181 deletions
13
README.md
13
README.md
|
|
@ -57,3 +57,16 @@ Angular CLI does not come with an end-to-end testing framework by default. You c
|
||||||
## Additional Resources
|
## Additional Resources
|
||||||
|
|
||||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||||
|
|
||||||
|
## Séquence 2 – Logique réactive du flux de données
|
||||||
|
### 1. Structure du flux
|
||||||
|
- Le service `TaskService` utilise un **BehaviorSubject** pour stocker et diffuser la liste des tâches.
|
||||||
|
- Le composant `Home` s’abonne à ce flux via `tasks$` et le **pipe async**.
|
||||||
|
### 2. Mise à jour des données
|
||||||
|
- La méthode `addTask()` ajoute une tâche puis appelle `next()` pour émettre la nouvelle liste.
|
||||||
|
- La méthode `removeTask()` supprime une tâche puis émet à nouveau la liste mise à jour.
|
||||||
|
- La vue est automatiquement réactualisée sans rechargement.
|
||||||
|
### 3. Points clés retenus
|
||||||
|
- Pas besoin d’appeler `getTasks()` à chaque fois : la donnée est **vivante**.
|
||||||
|
- `| async` gère l’abonnement et le désabonnement automatiquement.
|
||||||
|
- Le flux reste cohérent entre le service et la vue.
|
||||||
34
angular.json
34
angular.json
|
|
@ -11,7 +11,7 @@
|
||||||
"prefix": "app",
|
"prefix": "app",
|
||||||
"architect": {
|
"architect": {
|
||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular-devkit/build-angular:application",
|
"builder": "@angular/build:application",
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": "dist/task-board",
|
"outputPath": "dist/task-board",
|
||||||
"index": "src/index.html",
|
"index": "src/index.html",
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
"defaultConfiguration": "production"
|
"defaultConfiguration": "production"
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
"builder": "@angular/build:dev-server",
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
"buildTarget": "TaskBoard:build:production"
|
"buildTarget": "TaskBoard:build:production"
|
||||||
|
|
@ -68,10 +68,10 @@
|
||||||
"defaultConfiguration": "development"
|
"defaultConfiguration": "development"
|
||||||
},
|
},
|
||||||
"extract-i18n": {
|
"extract-i18n": {
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n"
|
"builder": "@angular/build:extract-i18n"
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
"builder": "@angular/build:karma",
|
||||||
"options": {
|
"options": {
|
||||||
"polyfills": [
|
"polyfills": [
|
||||||
"zone.js",
|
"zone.js",
|
||||||
|
|
@ -95,5 +95,31 @@
|
||||||
},
|
},
|
||||||
"cli": {
|
"cli": {
|
||||||
"analytics": false
|
"analytics": false
|
||||||
|
},
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"type": "component"
|
||||||
|
},
|
||||||
|
"@schematics/angular:directive": {
|
||||||
|
"type": "directive"
|
||||||
|
},
|
||||||
|
"@schematics/angular:service": {
|
||||||
|
"type": "service"
|
||||||
|
},
|
||||||
|
"@schematics/angular:guard": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:interceptor": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:module": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:pipe": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
},
|
||||||
|
"@schematics/angular:resolver": {
|
||||||
|
"typeSeparator": "."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9863
package-lock.json
generated
9863
package-lock.json
generated
File diff suppressed because it is too large
Load diff
22
package.json
22
package.json
|
|
@ -10,21 +10,21 @@
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/common": "^19.2.0",
|
"@angular/common": "^21.0.1",
|
||||||
"@angular/compiler": "^19.2.0",
|
"@angular/compiler": "^21.0.1",
|
||||||
"@angular/core": "^19.2.0",
|
"@angular/core": "^21.0.1",
|
||||||
"@angular/forms": "^19.2.0",
|
"@angular/forms": "^21.0.1",
|
||||||
"@angular/platform-browser": "^19.2.0",
|
"@angular/platform-browser": "^21.0.1",
|
||||||
"@angular/platform-browser-dynamic": "^19.2.0",
|
"@angular/platform-browser-dynamic": "^21.0.1",
|
||||||
"@angular/router": "^19.2.0",
|
"@angular/router": "^21.0.1",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"zone.js": "~0.15.0"
|
"zone.js": "~0.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^19.2.11",
|
"@angular/build": "^21.0.0",
|
||||||
"@angular/cli": "^19.2.11",
|
"@angular/cli": "^21.0.0",
|
||||||
"@angular/compiler-cli": "^19.2.0",
|
"@angular/compiler-cli": "^21.0.1",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"jasmine-core": "~5.6.0",
|
"jasmine-core": "~5.6.0",
|
||||||
"karma": "~6.4.0",
|
"karma": "~6.4.0",
|
||||||
|
|
@ -32,6 +32,6 @@
|
||||||
"karma-coverage": "~2.2.0",
|
"karma-coverage": "~2.2.0",
|
||||||
"karma-jasmine": "~5.1.0",
|
"karma-jasmine": "~5.1.0",
|
||||||
"karma-jasmine-html-reporter": "~2.1.0",
|
"karma-jasmine-html-reporter": "~2.1.0",
|
||||||
"typescript": "~5.7.2"
|
"typescript": "~5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1 +1,19 @@
|
||||||
<p>home works!</p>
|
<h1>Home works!</h1>
|
||||||
|
|
||||||
|
<h2>Task List</h2>
|
||||||
|
@if (tasks$ | async; as tasks) {
|
||||||
|
<ul>
|
||||||
|
@for (task of tasks; track task) {
|
||||||
|
<li>
|
||||||
|
{{ task.title }}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
} @else {
|
||||||
|
<p>Loading tasks...</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<p>Elapsed time: <span style="font-family:monospace;font-size:1.2em">{{ elapsedClock }}</span></p>
|
||||||
|
|
||||||
|
<!-- Add a task -->
|
||||||
|
<button (click)="addTask('New Task')">+ Add a task</button>
|
||||||
|
|
@ -1,11 +1,45 @@
|
||||||
import { Component } from '@angular/core';
|
|
||||||
|
import { Component, inject } from '@angular/core';
|
||||||
|
import { CommonModule, AsyncPipe } from '@angular/common';
|
||||||
|
import { TaskService } from '../../service/task.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
imports: [],
|
imports: [CommonModule, AsyncPipe],
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrl: './home.component.css'
|
styleUrl: './home.component.css'
|
||||||
})
|
})
|
||||||
export class HomeComponent {
|
export class HomeComponent {
|
||||||
|
protected count = 0;
|
||||||
|
private intervalId: any;
|
||||||
|
public elapsedClock: string = '';
|
||||||
|
|
||||||
|
private taskService = inject(TaskService);
|
||||||
|
tasks$ = this.taskService.tasks$;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.updateElapsedClock();
|
||||||
|
this.intervalId = setInterval(() => {
|
||||||
|
this.count++;
|
||||||
|
this.updateElapsedClock();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
if (this.intervalId) {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateElapsedClock() {
|
||||||
|
const totalSeconds = Math.floor(this.count / 2);
|
||||||
|
const h = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
|
||||||
|
const m = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
|
||||||
|
const s = String(totalSeconds % 60).padStart(2, '0');
|
||||||
|
this.elapsedClock = `${h}:${m}:${s}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTask(title: string) {
|
||||||
|
this.taskService.addTask(title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
16
src/app/service/task.service.spec.ts
Normal file
16
src/app/service/task.service.spec.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TaskService } from './task.service';
|
||||||
|
|
||||||
|
describe('TaskService', () => {
|
||||||
|
let service: TaskService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(TaskService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
28
src/app/service/task.service.ts
Normal file
28
src/app/service/task.service.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject, of } from 'rxjs';
|
||||||
|
import { delay } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class TaskService {
|
||||||
|
private tasks = [
|
||||||
|
{ id: 1, title: 'Sample Task' },
|
||||||
|
{ id: 2, title: 'Another Task' },
|
||||||
|
{ id: 3, title: 'More Tasks' }
|
||||||
|
];
|
||||||
|
private nextId = this.tasks.length > 0 ? Math.max(...this.tasks.map(t => t.id)) + 1 : 1;
|
||||||
|
|
||||||
|
getTasks() {
|
||||||
|
return of(this.tasks).pipe(delay(1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
private tasksSubject = new BehaviorSubject(this.tasks);
|
||||||
|
tasks$ = this.tasksSubject.asObservable();
|
||||||
|
|
||||||
|
addTask(title: string) {
|
||||||
|
const newTask = { id: this.nextId++, title: title.trim() };
|
||||||
|
this.tasks.push(newTask);
|
||||||
|
this.tasksSubject.next([...this.tasks]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue