Angular中从组件调用TodosService的addTodo方法不生效问题
Angular Todo应用组件调用addTodo后视图不更新的原因及解决方法
问题描述
我开发了一个采用Subject as a Service模式进行状态管理的Todo应用。TodosService中的addTodo方法在从组件调用时,虽然能执行(可打印新数组),但新增的Todo无法在视图中显示;而在服务内部构造函数中调用该方法时,视图能正常更新。
TodosService代码
import { Injectable } from '@angular/core'; import { Todo } from './todo.model'; import { HttpClient } from '@angular/common/http'; import { Observable, BehaviorSubject, map, pipe } from 'rxjs'; @Injectable() export class TodosService { private todos$ = new BehaviorSubject<Todo[]>([]); constructor(private http: HttpClient) { this.http .get<Todo[]>('https://jsonplaceholder.typicode.com/users/1/todos') .subscribe((todos) => { this.todos$.next(todos); }); } public getTodos(): Observable<Todo[]> { return this.todos$; } public getCompletedTodos(): Observable<Todo[]> { return this.todos$.pipe( map((todos) => todos.filter((todo) => todo.completed)) ); } public addTodo(event: string) { const newArray: Todo[] = [ ...this.todos$.value, { userId: 1, id: this.todos$.value.length, title: event, completed: false, }, ]; this.todos$.next(newArray); } }
AddTodo组件代码
import 'zone.js/dist/zone'; import { Component } from '@angular/core'; import { TodosService } from './todo.service'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-add-todo', standalone: true, imports: [CommonModule, FormsModule], providers: [TodosService], template: ` <label>Add Todo</label> <input type="text" [(ngModel)]="todo" /> <button (click)="addNewTodo()">Submit</button> `, }) export class AddTodo { todo: string = ''; constructor(private todoService: TodosService) {} addNewTodo() { this.todoService.addTodo(this.todo); this.todo = ''; } ngOnInit() {} }
服务内部调用正常的情况
当在服务构造函数中通过setTimeout调用addTodo时,视图更新正常:
constructor(private http: HttpClient) { this.http .get<Todo[]>('https://jsonplaceholder.typicode.com/users/1/todos') .subscribe((todos) => { this.todos$.next(todos); }); setTimeout(()=> { this.addTodo('Todo from inside setTimeout') }, 3000) }
原因分析
核心问题是服务实例不共享:
- 在
AddTodo组件的@Component装饰器的providers数组中声明了TodosService,这会让Angular为该组件及其子组件创建一个独立的服务实例。 - 显示Todo列表的组件使用的是另一个
TodosService实例(比如根注入器提供的实例),所以AddTodo组件调用自己实例的addTodo时,更新的是该实例内部的BehaviorSubject,列表组件订阅的是另一个实例的数据流,自然看不到更新。 - 服务内部构造函数调用
addTodo时,操作的是列表组件订阅的那个实例的BehaviorSubject,因此视图能正常更新。
解决方法
步骤1:确保服务全局单例
修改TodosService的@Injectable装饰器,添加providedIn: 'root',让Angular在根注入器中提供该服务,实现全局单例:
@Injectable({ providedIn: 'root' }) export class TodosService { // ... 原有代码不变 }
步骤2:移除组件内的服务声明
删除AddTodo组件@Component装饰器中的providers数组,确保组件使用根注入器的服务实例:
@Component({ selector: 'app-add-todo', standalone: true, imports: [CommonModule, FormsModule], // 移除providers: [TodosService] template: ` <label>Add Todo</label> <input type="text" [(ngModel)]="todo" /> <button (click)="addNewTodo()">Submit</button> `, }) export class AddTodo { // ... 原有代码不变 }
这样修改后,AddTodo组件和列表组件会使用同一个TodosService实例,调用addTodo时更新的数据流会被列表组件订阅到,视图就能正常更新了。
内容的提问来源于stack exchange,提问作者onTheInternet
相关产品推荐
相关产品推荐

