Search code examples
angulartypescripthooklifecycle

Unclear why parent subscribed to child's emitter before initialization in Angular


I've noticed that I have to instantiate the emitter already when declaring the fields in order to avoid errors like Cannot read property 'subscribe' of undefined. So the following's how I do it now and it seems to work well.

export class ShazooComponent implements OnInit {
  constructor() { }
  @Input() hazaa = new Hazaa();
  @Output() hazaaChange = new EventEmitter<Hazaa>();
  ngOnInit() { }
}

It also works if I instantiate the emitter in the constructor, which to me looks a bit better. However, since the subscriptions and other component related works related to the component are relevant first after it's been initialized, I expected that I could perform those things in ngOnInit() like this.

export class ShazooComponent implements OnInit {
  constructor() { }
  @Input() hazaa = new Hazaa();
  @Output() hazaaChange: EventEmitter<Hazaa>;
  ngOnInit() { this.hazaaChange = new EventEmitter<Hazaa>(); }
}

Apparently, it causes an exception being thrown. I've been reading the docs about the lifetime of the component and the hooks there. Perhaps, I'm not bright enough to grasp it. I need help understanding why it doesn't work and where it says so in the docs. I get that the parent component subscribes to the emitter before the child component has been initialized but that makes no sense to me. Why would we subscribe to something that we do know isn't inited (and can't emit jack, yet)?


Solution

  • Why would we subscribe to something that we do know isn't inited (and can't emit jack, yet)?

    You are using two-way-binding hazaa and hazaaChange, i.e [(hazaa)]="myHazaa". So when the parent is created, it creates your child component and instantly sees that you have two-way-binding, so means it tries to subscribe to it. At that point the parent only sees: hazaaChange: EventEmitter<Hazaa>, which means that hazaaChange is correctly errored out as undefined. You wouldn't get an error if you were to destruct the two-way-binding, but what two-way-binding does for you, is to call emit() for you, and in this case it that happens when parent has been created, but before hazaaChange has had time to be initialized in child OnInit.

    As a sidenote, at least in angular 8 a more userfriendly error is thrown instead of the error you are getting. Instead it errors out like...

    @Output hazaaChange not initialized in 'ShazooChildComponent'.
    

    I think you should just keep the intialization when the component is created, that way you know for sure that your eventemitter is not undefined.