ngIf 的组件初始化不会触发 ngOnInit

Posted

技术标签:

【中文标题】ngIf 的组件初始化不会触发 ngOnInit【英文标题】:Component initialization by ngIf does not trigger ngOnInit 【发布时间】:2020-01-29 07:33:23 【问题描述】:

我有一个父组件,其中包含一些基于 ngIf 评估呈现的子组件。

我使用的是 Angular 8.1.3

父组件

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;
  


及其模板

<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>

这是第二个子组件(未正确初始化的那个)

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"
         );

    


还有一个用于测试的缩小模板

<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>

父级从服务器检索 _stage 值,并正确显示第一个子级。 然后该组件通过 EventEmitter 将新的 _stage 值回传给其父级,父级相应地更新其变量的值。 新值 (2) 是实际上将我的第二个 ngIf 变为 true 的值,并且确实调用了第二个孩子(它称为第三阶段形式,不要混淆)构造函数。但是它的 ngOnInit 没有启动。 默认值“TEST”没有显示在我的输入字段中,通过 Augury 检查组件树,我可以看到构造函数注入的两个依赖项存在,而在 ngOnInit 中创建的表单组不存在。 如果我创建一个直接指向我的子组件的新路由,一切都会按预期工作。

【问题讨论】:

请堆叠闪电战 【参考方案1】:

您正在使用异步的可观察对象。当请求完成时,subscribe() 中的任何内容都会被调用,这可能随时发生。

所以,您的this.id 是在您已经在第二个 observable 上调用 subscribe 之后设置的。您需要使用一些地图来确保订阅的顺序,例如concatMap()

 ngOnInit() 
  this.route.params.pipe(
    concatMap(params => this.prodService.get(params.id))
  )

【讨论】:

在收到您的回复通知一年后,我再次提出了这个被遗忘的问题,对此事的了解较少,现在我意识到 Eliseo 的回答也是正确的。谢谢你的帮助。我会将他的问题标记为已接受,因为他先回答了。 别担心!我正在寻找其他东西并最终遇到了这个问题,所以我决定添加一个答案,并解释一下它为什么不起作用:D【参考方案2】:

哈维尔,当你写作时

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

你需要使用 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))
  );

【讨论】:

对不起,但这个答案与问题完全无关:我从路径中检索 id 没有问题,确实我的父组件工作,甚至它的第一个子组件也得到正确渲染。这也是不正确的:您的方法只是做同样事情的一种复杂方式:在我的代码中,this.id 从匿名函数中获取值。 @4javier,在您的代码中,当您调用 this.prodService.get 时,this.id 没有价值,抱歉,我没有阅读您的问题,但是如果你有一个 *ngIf,就会调用 ngOnInit,所以我认为这是错误 我可以保证不是这样:正如我已经说过的,匿名函数将 id 变量正确设置为 params.id 值。我只是检查确定。 花了一些时间(故意讽刺),但现在我知道你是对的。谢谢。

以上是关于ngIf 的组件初始化不会触发 ngOnInit的主要内容,如果未能解决你的问题,请参考以下文章

删除在 ngOnInit() 上订阅服务创建的空行

输出事件发射器仅在 ngOnInit 上触发

导航回组件时,ngOnInit 不会再次运行?

Angular Jasmine 测试未在 ngOnInit 中触发订阅

当同一个组件加载不同的数据时,不会调用 ngOnInit

ngOnInit 在 Angular 的组件内初始化 EventEmitter 变量是不是为时已晚?