Search code examples
angularangular-lifecycle-hooks

Angular child component not initialised when parent is function during parent oninit


I having a problem showing my alert when triggered in ResetPasswordComponent oninit. The subscribe block for AlertService is not being triggered however if I move the code from oninit to the constructor it will work. However it does not seem like it's a good practice to put initialization/declaration code in the constructor. So is there a better solution to this?

Parent

export class ResetPasswordComponent implements OnInit {
  public email: string;
  public token: string;

  constructor(public alertService: AlertService) {
  }

  ngOnInit() {
    this.token = this.route.snapshot.queryParamMap.get('token');
    this.email = this.route.snapshot.queryParamMap.get('email');
    console.log("Parent on init");
    if (this.token === null || this.token === "" ||
        this.email === null || this.email === "") {
      this.form.disable();
      this.alertService.error("Invalid link");
    }
  }
}

Child

export class AlertComponent implements OnInit, OnDestroy {
  private subscription: Subscription;
  public messages: string[];
  public type: string;

  constructor(private alertService: AlertService) {

  }

  ngOnInit() {
    console.log(this.alertService.getAlert());
    this.subscription = this.alertService.getAlert().subscribe(data => {
      console.log("OK");
      // do something
    });
    console.log("Child On init");
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

AlertService

@Injectable({ providedIn: 'root' })
export class AlertService {
  private subject = new Subject<Alert>();

  constructor(private router: Router) { }

  error(message: any, navigateTo = null) {
    if (navigateTo !== null) {
      this.navigateWithMessage(navigateTo, AlertType.error, message);
    }
    const alert: Alert = { type: AlertType.error, message };
    this.subject.next(alert);
    console.log("NEXT");
  }

  getAlert(): Observable<Alert> {
    return this.subject.asObservable();

}

Solution

  • I believe the problem is that the event is triggered before the child component has started subscribing in the ngOnInit. When the child component finally subscribes, the "Invalid link" event has already happened and is finished.

    There is a couple of solutions to this, one is changing your Subject to a BehaviorSubject, like:

    private subject = new BehaviorSubject<Alert>(null);

    Now, as soon as the child subscribes, it will receive the last event from the Observable, even after the event has happened.

    Another solution could be adding the shareReplay() operator to your observable. But in this specific case, when you are already using subjects, I would go with the BehaviorSubject solution.