【问题标题】:Angular Material Stepper with Separate Component for each step - ExpressionChangedAfterItHasBeenCheckedErrorAngular Material Stepper,每个步骤都有单独的组件 - ExpressionChangedAfterItHasBeenCheckedError
【发布时间】:2020-09-04 11:18:39
【问题描述】:

我有一个材料步进器,步进器的每个步骤都有表格。在那里,每个步骤都应该由与之关联的表单控制。

即使在 SO 中已经提出了这个问题,这些问题的答案也不能解决我的问题。所以我在问。

父 HTML

<mat-horizontal-stepper linear #stepper>
    <mat-step [stepControl]="frmStepOne">
        <ng-template matStepLabel>Step One Details</ng-template>
        <app-first-step #stepOne></app-first-step>
    </mat-step>
    <mat-step [stepControl]="frmStepTwo">
        <ng-template matStepLabel>Step Two Details</ng-template>
        <app-second-step #stepTwo></app-second-step>
    </mat-step>
</mat-horizontal-stepper> 

在我的父组件中,我有以下内容。

@ViewChild('stepOne') stepOneComponent: FirstStepComponent;
@ViewChild('stepTwo') stepTwoComponent: SecondStepComponent;


get frmStepOne() {
    return this.stepOneComponent ? this.stepOneComponent.frmStepOne : null;
}

get frmStepTwo() {
    return this.stepTwoComponent ? this.stepTwoComponent.frmStepTwo : null;
}

我的子类组件

frmStepOne: FormGroup;

constructor(private formBuilder: FormBuilder) {

    this.frmStepOne = this.formBuilder.group({
      name: ['', Validators.required]
    });
}

子类 HTML

<mat-card>

  <form [formGroup]="frmStepOne">

    <mat-form-field>
      <input matInput formControlName="name" matInput placeholder="Name" required>
    </mat-form-field>

    <mat-card-actions>
      <button mat-raised-button matStepperNext class="nav-btn pull-right">Next</button>
    </mat-card-actions>

  </form>

</mat-card>

现在,当我运行应用程序时,在控制台中,我看到以下内容。

错误错误:ExpressionChangedAfterItHasBeenCheckedError:表达式 检查后有变化。以前的值:'stepControl:null'。 当前值:'stepControl: [object Object]'。

正如我之前提到的,SO 中也有相同的讨论。例如,以下链接问题的答案和 cmets 建议将表单初始化移动到构造函数,而不是将其放在 onInit 中。我已经这样做了。

Angular Material Stepper Component For Each Step

但是,我仍然收到错误消息。

这是我在 Stackblitz 上的项目 - https://stackblitz.com/github/vigamage/stepper-component-wise

有人可以建议如何解决这个问题吗?

谢谢你..

【问题讨论】:

  • 还请修改您问题的标题。它与 Angular 材料及其步进器无关,更重要的是,我们需要感谢 Angular 在开发模式下向我们展示我们的代码有什么问题,因为在生产中你不会得到这个错误。
  • 请找到我提供的解决方案。我修改了您的代码以使错误消失link to the solution 无需使用更改检测..您欠我一个更改检测:)

标签: angular typescript angular-material angular9 angular-material-stepper


【解决方案1】:

基本上,角度在已经将两个值都设置为null 之后检测到变化。因此,您需要明确告诉 Angular 它应该运行更改检测。 Check out this demo code

我已经删除了使用get 的逻辑,因为直接从 HTML 调用函数是一种不好的做法。它会被多次调用。尝试将控制台日志放入您的代码中并查看。

我已使用ngAfterViewInitChangeDetectorRef 进行通知。

export class AppComponent implements AfterViewInit {
  title = 'mat-stepper';

  form1: FormGroup;
  form2: FormGroup;
  @ViewChild('stepOne') stepOneComponent: FirstStepComponent;
  @ViewChild('stepTwo') stepTwoComponent: SecondStepComponent;

  constructor(private cdr :ChangeDetectorRef){}

  ngAfterViewInit(){
    this.form1 = this.stepOneComponent.frmStepOne;
    this.form2 = this.stepTwoComponent.frmStepTwo
    this.cdr.detectChanges();
  }

}

这将解决错误

【讨论】:

  • 我认为您可以尝试的另一件事是 QueryList,它具有 change 功能,可作为订阅的可观察对象。也检查一下
  • 我说除非你必须,否则不要搞乱更改检测!试试我提供的解决方案,你会发现你不必这样做。
  • 现在我不知道该接受什么作为答案,因为两者都解决了我遇到的问题
  • @vigamage: 你有什么感觉 :D 并且看起来很简单。试试我提到的评论部分
  • @vigamage 请注意,我的回答为您节省了不必要的更改检测!这个问题会在你的许多应用组件中重复很多,在性能方面,我把计算留给你。
【解决方案2】:

只需将导致此错误的变量更改为Observablepipedelay(0) operator

这里是一个link 的有效解决方案 - 没有错误,也不需要使用变更检测。

这是一个例子

假设我在html 模板中使用了string 类型的变量name,它会导致上述错误。

<div>{{ name }}</div> // BOOM ERROR why? 

因为一开始name 是一个东西,在 Angular 已经检查过它之后它被改变了。

所以要解决这个问题..首先将name 更改为Observable,例如name$

name$: Observable<string>; // name$ replaces name    

private myName$ = new BehaviorSubject<string>("");
myNameListener$: Observable<string> = this.myName$.asObservable();
myName(name: string) {
    this.myName$.next(name)
}

现在在 ngOnInit() 只听那个 Observable

ngOnInit() {

    this.name$ = this.myNameListener$.pipe(
        delay(0)
    )

}

使用async 管道在html 模板中订阅Observable

<div *ngIf=" (name$ | async) as name ">{{ name }}</div> // render your variable

现在将您的实际数据传递到您想要的任何地方。

this.myName(data);

这里是一个link 一个有效的解决方案 - 没有错误,也不需要使用变更检测。

祝你好运!

【讨论】:

    【解决方案3】:

    处理这个问题的一个非常简单的方法是在子模板中声明表单组,在父模板中,您可以引用它!这样做的主要好处是它非常安全,并且比所有其他选项更干净。

    <mat-step [stepControl]="step1.step1Form">
      <app-step1 #step1></app-step1>
    </mat-step>
    

    【讨论】:

      猜你喜欢
      • 2018-07-08
      • 1970-01-01
      • 2019-09-28
      • 2019-03-27
      • 2020-05-18
      • 2018-07-14
      • 1970-01-01
      • 2019-04-01
      • 2019-04-18
      相关资源
      最近更新 更多