I have a todo application that is using the Subject as a Service approach to state management.
My todo service looks like this
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);
}
}
And my add-todo component looks likes this.
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() {}
}
I know that my addTodo method is being called by the component because I can console log the newArray value and see it when I try to add a new one. But for some reason my new todo isn't being displayed.
What's weirder is that this works fine when I call addTodo inside the constructor of my service. When done this way, my component that displays the todos updates properly.
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)
}
So why does my list-todo component not update when addTodo is called from a component, rather than inside the service itself?
I've created a stackblitz demo

As mentioned by @BizzyBob in the comments, if you provide
TodosServiceon component-level, each component will get their own instance of aTodosServiceand there will be no shared states between them.In your case you can solve this by providing
TodosServiceinside yourAppcomponent in yourmain.tsand removing the individual provider arrays from your standalone components.Your
Appcomponent will look like: