Reputation: 3347
At the moment I am trying to find a way to improve the update speed of a nested child component. The nested child component and the parent all get the data from a service. When I get a response from the API I update the service.items value. The items are a Map so they do not trigger the ng on changes in any of the child components when the value changes. I assume this is due to angulars change detection strategy. Since the items Map doesn't actually change, an inner property does, angular doesn't notice the change for ngOnChanges.
It takes a couple of seconds for the change to be represented in the browser. Maybe I am taking the wrong approach. Here is how my components are kind of represented.
export interface Post {
id: number,
title: string,
comments: Comment[]
}
@Injectable()
export class DataService {
post: Map<number, Post>
getPost() {
//makes api call to populate post
}
updatePostWithCommentFromSignalR() {
// Here I set up hub listener
// I get the data relatively fast
this.hub.on('ReceiveComment', (comment: Comment) => {
this.post.comments.push(comment)
}
}
}
@Component(
template: `<child-component [post]="dataService.post"></child-component>`
)
export class ParentComponent {
constructor(public dataService: DataService) {}
}
@Component({
template: `<nested-child-component [comments]="post.comments"></nested-child-component>`
})
export class ChildComponent {
@Input() post // This comes from the parent component
}
@Component({
template: '{{comments | json}}'
})
export class NestedChildComponent {
@Input() comments // This comes from the child component
//data seems to take a couple seconds to be displayed here even though in the service I get
// data right away
}
Im not sure if this is the best approach. I tried to extract the main idea of what I was trying to accomplish in a simple way. If more details are needed let me know. I just felt that it would be complicated if I put my actual code into here. But I would prefer to not have a lag between getting the comment and updating the screen. I'm sure I have to do something with changeDetection.
EDIT: Correction. NgDoCheck() fires around the same time as the component actually updates. I guess I would want the child to fire an update on the data change.
Upvotes: 0
Views: 44
Reputation: 3347
The solution I ended up going with is probably not the most efficient. After updating the value in my service from the signalR method I used the ApplicationRef to trigger a manual update.
My service looks like this.
@Injectable()
export class DataService {
post: Map<number, Post>
constructor(private appRef: ApplicatonRef) {}
getPost() {
//makes api call to populate post
}
updatePostWithCommentFromSignalR() {
this.hub.on('ReceiveComment', (comment: Comment) => {
this.post.comments.push(comment)
this.appRef.tick()
}
}
}
Unfortunately this runs an entire application wide change detection and I may look into using ChangeDetectorRef if I can manage to bring this logic into a component.
Upvotes: 0
Reputation: 18869
In your service, you can try updating immutably. Since arrays and objects are stored by reference, changing the reference (location in memory) of the array/object should make change detection kick in.
Service.
this.hub.on('ReceiveComment', (comment: Comment) => {
this.post = {
...this.post,
comments: [...this.post.comments, comment],
}
}
To make change detection more efficient in your child components, you can do:
@Component({
.....
changeDetection: ChangeDetectionStrategy.OnPush
})
This will only kick change detection when references of the @Input
change (change in location in memory) and therefore should be updated immutably (https://angular-2-training-book.rangle.io/change-detection/change_detection_strategy_onpush). Although this is overkill for me and can have unintended consequences.
Upvotes: 1