Petey
Petey

Reputation: 3347

Improve visual update speed of child component in Angular 8

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

Answers (2)

Petey
Petey

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

AliF50
AliF50

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

Related Questions