简体   繁体   English

Angular Reactive Form - 使用 OnPush 时,setErrors 不更新 FormArray 内 FormControls 的视图

[英]Angular Reactive Form - setErrors not updating view for FormControls inside FormArray when OnPush is used

It is an interesting behaviour I tried in multiple projects and gave up using OnPush for FormArray, more specific it is an arry of row of FormGrop contains FormControl .这是一个有趣的行为,我在多个项目中尝试过,并放弃了将 OnPush 用于 FormArray,更具体地说,它是FormGrop 的一行包含 FormControl However, I think it is time to raise a question here so that it maybe same issue for others (or just me too stupid that forget a simple tiny line of code, please point it out if so, thanks), I created a mockup here: stackblitz但是,我认为是时候在这里提出一个问题,以便其他人也可能遇到同样的问题(或者只是我太愚蠢而忘记了一行简单的代码,如果是的话请指出,谢谢),我在这里创建了一个模型: 堆栈闪电战

For some reason my project using OnPush for all such dummy componnents which needs manually call ChangeDetectionRef.markForCheck if a View Update is required.出于某种原因,我的项目对所有此类虚拟组件使用OnPush ,如果需要视图更新,则需要手动调用ChangeDetectionRef.markForCheck But this time wherever I put the mark it doesn't show any differece.但是这次无论我把标记放在哪里,它都没有显示出任何差异。

Generally, I created a save$ Subject to do a validation on the fly and save the whole Grid to somewhere if it is valid .通常,我创建了一个save$ Subject 来即时进行验证,如果valid ,则将整个 Grid保存到某个地方。 Now, since save$ is a BehaviourSubject which means it is called once the form is built.现在,由于save$是一个 BehaviourSubject,这意味着一旦构建了表单就会调用它。 You can see the whole FormGroup is invalid on the screen but all cells are valid , which is weird.您可以在屏幕上看到整个FormGroupinvalid的,但所有单元格都是valid的,这很奇怪。 在此处输入图像描述

The reason I mentioned about "non onpush" is because with that mode, Angular runs CD for you.我提到“非 onpush”的原因是因为在这种模式下,Angular 会为你运行 CD。 And if your code works as expected in default mode, it means, your "onpush" code just needed to trigger CD.如果您的代码在默认模式下按预期工作,则意味着您的“onpush”代码只需要触发 CD。
I didn't mean to tell you that you should switch to default mode.我并不是要告诉您应该切换到默认模式。

For why it didn't work in your original question:为什么它在您的原始问题中不起作用:
The form itself, by default run updateValueAndValidity after initialized, you don't declare any validators => the form status is valid;表单本身,默认情况下在初始化后运行updateValueAndValidity ,您不声明任何验证器 => 表单状态有效; then you call setErrors which behind the scene, set the from status to false => this cause the inconsistency in the view, hence the ng100 error.然后你在幕后调用 setErrors,将 from status 设置为 false => 这会导致视图不一致,因此会出现 ng100 错误。

//Edit: //编辑:
I debugged the flow again and found out that after each control gets added to the form, it will run the updateValueAndValidity function again.我再次调试流程,发现每个控件添加到窗体后,它都会再次运行 updateValueAndValidity 函数。 Which means, the setErrors will cause the form to be Invalid first, then updateValueAndValidity will make it all Valid again.这意味着,setErrors 将首先导致表单无效,然后 updateValueAndValidity 将使它再次有效。 The inconsistency is still there, just the other way around.不一致仍然存在,只是反过来。
//End Edit //结束编辑

The dirty hack for this is using setTimeout, which I personally don't like and don't recommend.肮脏的 hack 是使用 setTimeout,我个人不喜欢也不推荐。 But to give a properly solution, it will require to know the business logic in the code.但是要给出一个合适的解决方案,就需要了解代码中的业务逻辑。

Here it is the code I posted in the comment.这是我在评论中发布的代码。

setTimeout(() => {
        control.setErrors(Object.keys(errors).length ? errors : null);        
});

This helps to schedule the setError (which actually, the valid status) to run in the next CD round.这有助于安排 setError(实际上是有效状态)在下一轮 CD 中运行。
This code doesn't have any CD yet.此代码还没有任何 CD。 So yes, the form is valid all the time => That's the reason I tell you to create a button to see the CD in action.所以是的,表格始终有效 => 这就是我告诉您创建一个按钮来查看 CD 的原因。

Now we wouldn't want a new button for users to click to see the form update.现在我们不希望用户点击一个新按钮来查看表单更新。 We need to run the CD in code.我们需要在代码中运行 CD。 So where to put it?那么放在哪里呢?
We know the setError trigger the form status so we will schedule it later, and where does the setError get invoked?我们知道 setError 会触发表单状态,所以我们稍后会安排它,setError 在哪里被调用? customValidator , we will schedule this function to run later so the final code to modify is here: customValidator ,我们将安排此功能稍后运行,因此最终要修改的代码在这里:

//instead of run setTimeout in a loop
//we make sure that all setError is scheduled in one run
setTimeout(() => {
      customValidator()(form);
      this.cd.markForCheck();
    })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 .setErrors 不是函数,对于 Angular 中的 FormControls - .setErrors is not a function, for FormControls in Angular 如何在 angular 中访问 formarray 中的表单控件 - how to acess formcontrols inside formarray in angular 角度反应形式在另一个 FormGroup 内的另一个 FormArray 内的嵌套 FormGroup 内添加 FormArray - angular reactive form add FormArray inside a nested FormGroup inside another FormArray inside another FormGroup Angular Reactive Form中的FormArray不会在表单提交时重置 - FormArray in Angular Reactive Form not resetting on form submit 何时可以使用Angular onpush通知策略? - When can the Angular onpush notification strategy be used? 如何在Angular反应形式的FormGroup中的FormArray上进行迭代 - How to iterate over FormArray in FormGroup in an Angular reactive Form 如何在Angular的FormArray反应形式中获得唯一值? - How to get unique value in FormArray reactive form in Angular? 具有反应形式和 formArray 的表中的角度列级过滤器 - Angular Column level filters in table having reactive form and formArray Angular 反应形式 - 复选框 - 使用现有数组初始化 formArray - Angular reactive form - checkboxes - Initialize formArray with exisitng array Angular 反应式 Forms - FormArray 和 pacthValue - Angular Reactive Forms - FormArray and pacthValue
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM