I have this issue in a fairly complex web appp. I have tried to recreate it in simple form to share here but I can't so I assume its something specific to my build and probably won't get an answer but I'm posting because I've been at this for two days now and I'm getting desperate.
I have a service that defines an array. The service is injected into a component and I bind the display to that array
@for(item of myService.myArr; track $index;){
<div>{{item}}</div>
}
Other components also have the service injected and they later call a method of it that adds a new item to the array using a simple Array.push(). The array is definitely updated but when this happens it doesn't seem to trigger change detection and my display is not update to show the newly added item.
If I then do anything else within the app, like perform some other interaction like clicking a button, then the view updates and I see the newly added items.
I can also get it to work by using .detectChanges() but this seems hacky as this issue is happening basically everywhere I have bound data so my code is absolutely full of .detectChages() which suggests a deeper issue right?
I have tried using signal to do this but the same issue arises.
So is there some issue with binding a view to an array stored in a service?
Is there some detail of change detection or data binding that I'm that I'm missing or misunderstanding?
Any help and suggestions would be great. Thanks all.
I got the same problem and although I hope your problem had been fixed I leave here my solution anyway. I want to emphasise that maybe my problem is a bit different, but it had the same symptoms.
The Problem
I had some components done before, in angular 16, with some logic like:
data: DataObject[] = [];
constructor(someService: SomeService) {
subscription = someService.getData$(data => this.data = [...data]);
}
and the html add a child consuming the data array reacting to the data changes
<app-child [data]="data"></app-child>
and it worked fine triggering the ngOnChanges method, then I copied this component to another project in version 17 and it stoped working.
I refactored to use signals because angular 17 is all about signals and got the same problem. I'm new to Server Side Rendering (SSR) so I recreated the project without SSR and had the same weird behaviour.
The solution
The data that the component receive through the getData$() method comes from Server Side Events (SSE) and the flow is, I received an event, I add the data to an array and then I have an emitter that emits the update array ,pretty standard, I thought.
As I searched about the Changes about the Angular Changes Detection System (after seeing this thread) I found out that some stuff can run outside the angular context, for example web sockets (reference: https://www.linkedin.com/pulse/key-takeaways-from-playing-angular-17-change-detection-filip-flora-v7t7f/) I didn't know that, but it felt exactly that is was what was going on.
I implemented my "SSE client" from MDN Web Docs https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events but when I searched by "angular server side events" and found this article https://medium.com/@andrewkoliaka/implementing-server-sent-events-in-angular-a5e40617cb78 and when I saw that SSEs also run outside the angular context and don't trigger change detection.
So to fix this I had to import NgZone (zone.js is, until angular 18, what angular uses to do the automatic change detection) into my service and use it to emit my updated data array:
SomeService {
data = []
emiter = new EventEmitter()
constructor(private zone: NgZone) {
subscribeData()
}
private subscribeData() {
let eventSource = new EventSource('/api');
eventSource.onmessage = (event) => {
this.data.push(JSON.parse(event.data) as DataObject)
this.zone.run(() => this.emitter.emit(this.data))
}
}
getData$() {...}
}
The this.zone.run(() => ...) lets Angular know that the observable had changed and it need to handle the changes wherever its needed.
Sorry about all the introductory stuff, but it was important to let people know that it can be a solution for this use case, not for all use cases. And I mentioned Angular 16 and 17 because of context and because I believe that this types of problems have to do with a major refactoring that angular team is doing to its change detection system.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With