Search code examples
angularangular-ng-ifngoninit

Component initialization by ngIf does not trigger ngOnInit


I have a parent component with some childs that get rendered based on ngIf evaluation.

I'm on Angular 8.1.3

The parent component

import { ActivatedRoute } from '@angular/router';
import {FirstStageViewComponent} from "src/app/first-stage-view/first-stage-view.component";
import {SecondStageViewComponent} from "src/app/second-stage-view/second-stage-view.component";
import {SecondStageFormComponent} from "src/app/second-stage-form/second-stage-form.component";
import {ThirdStageFormComponent} from "src/app/third-stage-form/third-stage-form.component";
import {ThirdStageViewComponent} from "src/app/third-stage-view/third-stage-view.component";
import { ProdService } from "src/app/service/prod-service";


@Component({
  selector: 'app-prod-view',
  templateUrl: './prod-view.component.html',
  styleUrls: ['./prod-view.component.css']
})
export class ProdViewComponent implements OnInit 
{

  id;
  _stage: string;
  constructor(private prodService: ProdService, private route: ActivatedRoute) { }

  ngOnInit() {

    this.route.params.subscribe(params => this.id=params.id);

    this.prodService.get(this.id).subscribe((data: string) => (this._stage = data["_stage"]),
                                           error => (console.log(error)));

  }

  talkBack(_updatedStage: string) {

    this._stage=_updatedStage;
  }

}

and its template

<div class="container">


    <div *ngIf="_stage>0">
        <app-first-stage-view [id]=id></app-first-stage-view>
    </div>

    <div *ngIf="_stage==1 else stage2">
        <app-second-stage-form [id]=id (talk)=talkBack($event)></app-second-stage-form>
    </div>
    <ng-template #stage2>
    <div *ngIf="_stage>1">
        <app-second-stage-view [id]=id></app-second-stage-view>
    </div>
    </ng-template>


    <div *ngIf="_stage==2 else stage3">
        <app-third-stage-form [id]=id (talk)=talkBack($event)></app-third-stage-form>
    </div>
    <ng-template #stage3>
    <div *ngIf="_stage>2">
        <app-third-stage-view [id]=id></app-third-stage-view>
    </div>
    </ng-template>
</div>

This is the second child component (the one that doesn't get correctly initialized)

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, FormBuilder, FormArray, Validators } from "@angular/forms";
import { ThirdStageService } from "src/app/service/third-stage.service";

@Component( {
    selector: 'app-third-stage-form',
    templateUrl: './third-stage-form.component.html',
    styleUrls: ['./third-stage-form.component.css']
} )
export class ThirdStageFormComponent implements OnInit {

    tsForm: FormGroup;
    @Input() id: string;
    @Output() talk: EventEmitter<string> = new EventEmitter<string>();

    constructor( private tsService: ThirdStageService,
        private formBuilder: FormBuilder ) {}

    ngOnInit() {
        this.tsForm = this.formBuilder.group( {
            ingredient: "TEST"
        } );

    }

}

And a shrunk template for testing

<div class="container">
    <form [formGroup]="tsForm" (ngSubmit)="onSubmit()">
        <div class="form-group">
            <label for="name">{{ tsForm.controls[0].value }}</label> <input type="text"
                class="form-control" id="ingredient" formControlName="ingredient"
                required>
        </div>
    </form>
</div>

The parent retrieve the _stage value from server, and the first child gets correctly displayed.
Then this component talkback the new _stage value to its parent by an EventEmitter, and the parent update the value of its variable accordingly.
The new value (2) is the one that actually turns my second ngIf to true, and indeed second child (it's called third-stage-form, don't get confused) constructor gets called. But its ngOnInit doesn't fire up.
The default value "TEST" doesn't get shown inside my input field, and inspecting component tree through Augury, I can see that the two dependencies injected by constructor are present, while the formgroup created inside ngOnInit, is not.
If I create a new route pointing directly to my child component, everything works as expected.


Solution

  • Javier, when you write

    this.route.params.subscribe(params => this.id=params.id);
    //here this.id has no value
    this.prodService.get(this.id).subscribe(...)
    

    You need use swithMap

    this.route.params.pipe(
      switchMap((params)=>{ 
        //here you has "params" 
        this.id=params.id
        //we want return 
        return this.prodService.get(this.id)
      })
      ).subscribe((data: string) => (this._stage = data["_stage"]),
                  error => (console.log(error))
      );