Maison > Questions et réponses > le corps du texte
J'essaie de créer une application TODO simple en utilisant RXJS. J'ai une base de données fictive de serveur JSON contenant des tâches TODO.
Je me suis donc retrouvé avec ce TasksService :
@Injectable({ providedIn: 'root' }) export class TasksService { private _tasks : ITask[] = []; private _tasks$: BehaviorSubject<ITask[]> = new BehaviorSubject<ITask[]>([]); constructor (private _http: HttpClient) { } public getTasks() { this.getTasksObservableFromDb().pipe( tap( (tasks) => { this._tasks = tasks; this._tasks$.next(tasks); } ) ).subscribe(); return this._tasks$; } public addTask(task: ITask) { this._tasks.push(task); this._tasks$.next(this._tasks); } private getTasksObservableFromDb(): Observable<any> { return this._http.get<any>('http://127.0.0.1:3000/tasks'); }
Lorsque j'ajoute des tâches, je ne souhaite pas les publier immédiatement sur le serveur. Ainsi, lorsque je reçois des tâches du serveur, je les enregistre dans la propriété _tasks, puis je les transmets à la méthode next() de mon _tasks$:BehaviorSubject . Parce que plus tard, je veux publier par lots mes tâches sur le serveur et maintenant je veux juste qu'elles s'affichent correctement dans Angular.
Dans mon AppComponent, je récupère les tâches et les affecte à ma propriété de tâche.
export class AppComponent implements OnInit { public tasks!:BehaviorSubject<ITask[]>; constructor (private _tasksService: TasksService) {} ngOnInit(): void { console.log('OnInit'); this.tasks = this._tasksService.getTasks(); } public addTask() { this._tasksService.addTask( { id: crypto.randomUUID(), isImportant: true, text: 'Added task' } ); } }
Dans mon modèle HTML, j'utilise un canal asynchrone comme attribut de tâche et j'affiche ma tâche :
<ng-container *ngFor="let task of tasks | async"> {{task.text}} {{task.id}} </ng-container> <button type="button" (click)="addTask()">Add Task</button>
Mais j'ai accidentellement supprimé cette ligne dans mon TaskService :
this._tasks$.next(this._tasks);
Ma méthode ressemble maintenant à ceci :
public addTask(task: ITask) { this._tasks.push(task); }
Mais l'ajout de tâches fonctionne toujours ! Même si je ne transmets pas un ensemble de nouvelles tâches à mon BehaviorSubject, Angular affiche les tâches nouvellement ajoutées.
J'ai donc décidé d'enregistrer les valeurs dans mes tâches ! : Propriété BehaviorSubject<ITask[]> dans ma classe AppComponent :
public addTask() { this._tasksService.addTask( { id: crypto.randomUUID(), isImportant: true, text: 'Added task' } ); this.tasks.pipe(tap((value) => console.log(value) )).subscribe(); }
et les tâches sont ajoutées comme prévu - chaque fois que vous obtenez un tableau contenant une tâche :
Array(3) [ {…}, {…}, {…} ] <- Add task button is clicked Array(4) [ {…}, {…}, {…}, {…} ] <- Add task button is clicked Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- Add task button is clicked
Mais quand je renvoie cette ligne à la méthode addTask dans TaskService :
this._tasks$.next(this._tasks);
J'ai ces journaux :
Array(3) [ {…}, {…}, {…} ] <- Add task button is clicked -> one task is added Array(4) [ {…}, {…}, {…}, {…} ] <- Add task button is clicked -> one task is added Array(4) [ {…}, {…}, {…}, {…} ] <- I get the same array Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- Add task button is clicked -> one task is added Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- I get the same array Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- I get the same array once again
Donc je suis un peu perdu quant à la raison pour laquelle l'observable se comporte comme ça... peut-être que je ne comprends pas bien la méthode next() ?
P粉0806439752024-03-22 00:14:34
Autant que je sache, il y a deux problèmes avec ce code. Premièrement, vous ne savez pas pourquoi les journaux de la console contiennent des doublons. Deuxièmement, même si vous n'avez pas appelé ".next()" sur le sujet du comportement, vous ne savez pas pourquoi la vue a été mise à jour.
Commençons par le premier.
Vous devez comprendre comment fonctionnent les observables rxjs et les BehaviorSubjects. Un observable normal, lorsque vous y souscrivez, attendra qu'une certaine valeur soit émise, puis à chaque fois que cela se produit, il appellera l'opération que vous lui avez attachée. Par exemple :
exampleSubject = new Subject(); ngOnInit(): void { this.exampleSubject.pipe( tap(console.log) ).subscribe(); } emitValue() { this.exampleSubject.next("text"); }
Maintenant, veuillez noter que dans ce code, nous ne nous abonnons qu'une seule fois dans ngOnInit. Malgré cela, console.log est appelé à chaque fois que la méthode émetValue() est appelée (par exemple à partir d'un bouton). En effet, l'abonnement dure jusqu'à sa désinscription. Cela signifie que l'opération sera appelée à chaque fois que next() sera appelé sur le sujet.
Alors, que se passe-t-il lorsque vous vous abonnez plusieurs fois ? Essayons :
exampleSubject = new Subject(); ngOnInit(): void { subscribeToSubject(); // 1st subscribeToSubject(); // 2nd subscribeToSubject(); // 3rd this.emitValue(); } emitValue() { this.exampleSubject.next("text"); } subscribeToSubject() { this.exampleSubject.pipe( tap(console.log) ).subscribe(); }
Nous nous sommes abonnés à un sujet 3 fois et désormais chaque fois qu'une valeur est émise le log de la console est appelé 3 fois. Il est appelé pour chaque abonnement que vous créez.
Maintenant, lorsque nous regardons votre exemple, l'abonnement est ajouté à chaque fois que vous cliquez sur le bouton addTask. C'est pourquoi chaque fois que vous ajoutez une tâche, un journal de console supplémentaire apparaît. Mais bon, dans votre premier exemple, lorsque vous supprimez .next(), vous avez des journaux de console même si vous n'émettez aucune valeur, pourquoi ? Venons-en maintenant à BehaviorSubject. C'est un sujet spécial qui garde sa valeur et l'émet dès que vous vous abonnez. Et il émet également chaque fois que vous appelez .next() mais vous ne le faites pas, c'est pourquoi il appelle 1 journal de console à chaque fois que vous vous abonnez.
Ce que vous devriez faire, c'est appeler
this.tasks.pipe(tap((value) => console.log(value) )).subscribe();
Une seule fois, par exemple dans ngOnInit()
D'accord, nous entrons maintenant dans la deuxième phase
C'est très simple mais nécessite une certaine connaissance du fonctionnement des références.
Dans votre service, vous placez l'ensemble des tâches dans un BehaviorSubject. En fait, ce sujet fait référence à ce tableau. Cela signifie que chaque fois que vous insérez une nouvelle valeur dans le tableau, le BehaviorSubject en sera également propriétaire. C'est ce qui se produit, chaque fois que vous appelez la méthode addTask, une nouvelle valeur est poussée vers le tableau des tâches, et votre BehaviorSubject l'a également.