Angular - Input data not immediately available on child component, why?
Asked Answered
P

1

7

I have a child Calendar Component that receives events from his father through an input field.

@Input() private events: any[];

When the month changes the parent gets new events from an API Service and calls the child component to show them.

private populateEventsForCurrentMonth() {
return this.calendarService.getEventsAsAdmin(this.getCurrentStartDate(), 
this.getCurrentEndDate())
.then((evts) => {
  this.events = evts;
  this.calendarChild.selectEventDaysIfApplies();
})
.catch((err) => {
  console.log('error getting events', err);
});

}

But when the child tries to access the events they haven't been updated!

My workaround was wrapping the child function in a timeout of 0 milliseconds to put the entire execution at the end of the event loop.

public selectEventDaysIfApplies() {
setTimeout(() => {
  if (this.events) {
    this.events.forEach((event) => {
      let eventDate = new Date(event.starts_at);
      if (this.datesBelongToSameMonth(eventDate, this.currentDate)) {
        let dayWithEvent = this.days.find( (day) => {
          return day.number == eventDate.getDate();
        });
        if (!dayWithEvent.selected) {
          dayWithEvent.hasEvent = true;
        }
      }
    });
  }
}, 0);

}

Is there a better way of doing it? Maybe an Angular best practice that I should be implementing?

Thanks!

Pouched answered 29/8, 2017 at 15:33 Comment(3)
The data is not available in the child component because the angular watcher did not treated it yet. The setTimeout is a good way to handle it. So angular can propagate your change.Carminecarmita
.then((evts) => { means "later" or "eventually". The method is async because it takes a certain amount of time to get the result, the browser (JS) is not blocking until the data arrives, but instead continues execution, and when the data finally arrives, the function passed to then(...) is executed.Eloign
I think using the ngOnChanges() and use the SimpleChange object, Angular should know when the data is ready. It seems better than a setTimeout().Nidianidicolous
E
4

The data is not available yet, because this function is handled synchronously:

this.events = evts;
this.calendarChild.selectEventDaysIfApplies();

Immediately, after this.events has been set, the child components runs its method selectEventDaysIfApplies(). That is before the Angular change detection is run.

In my opinion, the parent component should not know about any checks and modifications its child components have to do.

A better solution would be to use the lifecycle hook OnChanges, which is triggered whenever an input of the component changes.

ngOnChanges(changes: SimpleChanges) {
  this.selectEventDaysIfApplies();
}

If your component has more than one input, you can check which one was changed.

Read more about this in the docs.

Eleen answered 29/8, 2017 at 18:49 Comment(1)
Thank you Kim! I like your approach. My original idea was doing something like this, without accessing the child. The only things I don't like is how similar it is to a watch in AngularJS, at the end we ended up doing almost the same things.Pouched

© 2022 - 2024 — McMap. All rights reserved.