当 Angular 4 未在 NgIf 中呈现时,删除验证

Posted

技术标签:

【中文标题】当 Angular 4 未在 NgIf 中呈现时,删除验证【英文标题】:Remove validation in angular 4 when it's not render in NgIf 【发布时间】:2018-10-03 00:51:51 【问题描述】:

我想删除未使用 NgIf 呈现控件的验证。我尝试使用指令来删除隐藏控件,但不能这样做,因为它没有在模板中呈现。所以我不能在指令中用 ElementRef 检查 formControlName。这是ts文件

this.form = this._fb.group(
  text1: ['', Validators.required],
  text2:  ['', Validators.required]
);

和模板

<form[formGroup]="form">
  <input type="text" formControlName="text1">
 <div *ngIf="false">
  <input type="text" formControlName="text2">
</div>

我想动态地和全局地删除 text2 的验证。不删除 ts 文件中的验证器。

【问题讨论】:

您也应该输入代码,以便其他人可以帮助您,而不仅仅是在文本中说明问题。 我只是将我的代码放入文件中并进行编辑。请帮我看看这个 【参考方案1】:

Kara 的这个 Angular 源 GitHub 问题评论似乎非常相关,并说明了如何通过将反应模型视为“事实来源”并根据该事实来源创建 ngIf 表达式来解决问题,而不是相反。这表明它是设计使然,您必须努力避免混淆模板驱动和反应式表单的想法。

https://github.com/angular/angular/issues/7970#issuecomment-228624899

感谢您花时间描述问题。我看了看 您的示例代码,似乎您正在使用反应形式 指令(又名“模型驱动”指令:ngFormModel 等),但是 模板驱动的策略。 ngIf 没有删除 来自表单的序列化和验证的控制实际上是由 设计,原因如下。

在每个表单范式中——模板驱动和反应式——只有 成为活动控件列表的真实来源之一。在里面 模板驱动范式,真理的源泉就是模板。在里面 反应式等价物,事实的来源是创建的表单模型 父组件。 DOM 并不规定表单的状态。 因此,如果您从 DOM 中删除表单控制元素 在使用反应式方法时,表单控件不一定 除非您希望它们发生变化,否则真相的来源会发生变化。你可以 选择通过调用命令式地更新控件 this.form.removeControl('controlName'),或者你可以选择保留 表单中的控件。这种灵活性允许您添加或删除 暂时来自 DOM 的输入,同时保持它们的表单值 序列化(例如,如果您有许多可折叠的部分到您的 表单,您可以在不影响值的情况下删除折叠部分 您的表格)。我们不想限制这种灵活性,并且 通过强制模型始终匹配 DOM 使所有权复杂化。

因此,在您的情况下,如果您选择被动策略,您需要 反转你的逻辑以依赖真相的来源——模型。 具体来说,这意味着在 模型通过调用 this.form.removeControl('name') 当按钮是 点击。然后,ngIf 应该取决于控件在 带有 *ngIf="form.contains('name')" 的模型,而不是其他方式 大约。在此处查看示例 plunker: http://plnkr.co/edit/V7bCFLSIEKTuxU9jcp6v?p=preview

值得注意的是,如果您仍在使用 beta.14(如您的 plunker),你需要调用 this.form.updateValueAndValidity() 手动。此要求已在 #9097 中删除,因此之后的版本 RC.2 不需要调用。

另一种选择是转换为模板驱动策略(否 ngFormModel), 它将在窗体中删除控件 从模板中销毁。例子: http://plnkr.co/edit/s9QWy9T8azQoTZKdm7uI?p=preview

我将关闭这个问题,因为它按预期工作,但我认为我们 可以使体验更加友好。一个好的开始会是一些 文档中包含更多食谱和指南。

【讨论】:

这就是我要找的。感谢您的帮助。加油!【参考方案2】:

我做了什么(并为我工作),使用另一个按钮 [已禁用] 创建一个替代 formgroupcontrol,管理按钮和表单的 *ngIf。

<mat-step [stepControl]="listBrandFormGroup">
    <form [formGroup]="listBrandFormGroup">
      <ng-template matStepLabel>Define tu marca</ng-template>

      <div class="heading">¡ Haber ! Definamos tu marca</div>
      <div class="subheading">Estamos a punto de hacer magia, solo necesitamos lo siguiente:</div>

      <div class="content" fxLayout="column" fxLayoutGap="8px" *ngIf="listBrand.length > 0">
        <mat-form-field fxFlex="auto">
          <mat-select name="brand_id" formControlName="brand_id" placeholder="Selecciona una marca existente" (selectionChange)="setBrand($event.value);">
            <mat-option [value]="0">Crear una nueva marca</mat-option>
            <mat-option *ngFor="let marca of listBrand" [value]="marca.id">marca.display_name</mat-option>
          </mat-select>
          <mat-hint>descripBrand</mat-hint>
        </mat-form-field>
      </div>
    </form>
    <form [formGroup]="brandFormGroup">
      <div class="content" fxLayout="column" fxLayoutGap="8px" *ngIf="idBrand === 0">
        <mat-form-field>
          <mat-label>Marca</mat-label>
          <input matInput formControlName="display_name" required>
          <mat-hint>Ese increíble y único nombre, ¡ tú sabes !</mat-hint>
        </mat-form-field>

        <mat-form-field fxFlex="grow">
          <mat-label>Descripción</mat-label>
          <textarea matInput formControlName="descrip" required></textarea>
          <mat-hint>¿ Cuéntanos de que se trata ?</mat-hint>
        </mat-form-field>

          <mat-label>Logo</mat-label>
          <input type="file" name="photo" ng2FileSelect required formControlName="display_logo" />
      </div>

      <div class="actions" fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px">
        <button mat-button type="button" (click)="stepper.reset()" [disabled]="brandFormGroup.pristine"
                color="primary">RESET
        </button>
        <button mat-raised-button matStepperNext color="primary"  [disabled]="brandFormGroup.invalid" *ngIf="idBrand === 0">SIGUIENTE</button>
        <button mat-raised-button matStepperNext color="primary"  [disabled]="listBrandFormGroup.invalid" *ngIf="idBrand > 0">SIGUIENTE</button>
        <button mat-raised-button matStepperNext color="primary"  [disabled]="listBrandFormGroup.invalid" *ngIf="idBrand > 0" (click)="launch();"><i class="material-icons">launch</i>LANCÉMONOS</button>
      </div>
    </form>
  </mat-step>

【讨论】:

【参考方案3】:

当条件属性改变时,动态调用方法来设置和删除验证。例如,

whenConditionChanges(condition:boolean)
  if(!condition)
    this.form.controls["text2"].setValidators([Validators.required]);
    this.form.controls["text2"].updateValueAndValidity();
   else 
    this.form.controls["text2"].setValidators(null);
    this.form.controls["text2"].updateValueAndValidity();
  

【讨论】:

我可以手动更改验证器。但是控制太多了。对于任何更改请求,我需要更新表单验证器。我的表单太复杂,无法以这种方式实现。根据表单中某些控件的值,大约有 10 多个条件。 我希望这是设置和删除验证器以动态控制表单的方法。【参考方案4】:

Here 示例:在运行时,您可以根据复选框值更新验证器。您可以根据需要设置字段并删除。

http://plnkr.co/edit/YMh0H61LxPGCFtm9Yl13?p=preview

【讨论】:

对我来说,在选择更改后调用updateValueAndValidity() 的简单事实解决了这个问题。我从一开始就将Validators.required 放在两个字段上,然后我只检查isEmployeeChanged() this.personForm.controls["myFirstComponent"].updateValueAndValidity(); this.personForm.controls["mySecondComponent"].updateValueAndValidity(); 【参考方案5】:

因为,您的 formcontrol text2 取决于某些条件。它不应该按要求控制。所以你的反应式表单控制应该是

this.form = this._fb.group(
  text1: ['', Validators.required],
  text2:  ['',]
);

如果有这样的场景,您希望确保文本在 dom 中出现时都需要它,然后在 Angular 中使用自定义验证器。请参阅documentation 以进行实施。

【讨论】:

所以我需要删除组件中的所有验证器并将所需的属性放入模板?假设我的所有字段都是必需的(如果可见) 不,您是否关注过上面共享的文档链接? 我的情况不一样。 Text1 和 text2 的可见性基于服务器数据。假设从服务器返回的数据是 enableControl: ["text1", "text3", ...]。如何使用提交和保存两种按钮来实现这一点。 此外,在某些情况下,某些控件的可见性基于另一个控件值。如何在不设置验证器或删除验证器的情况下处理我的表单。

以上是关于当 Angular 4 未在 NgIf 中呈现时,删除验证的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2+ - 当 ngIf 导致隐藏时将 ngModel 设置为 null

Angular (4, 5, 6, 7) - ngIf 上滑入滑出动画的简单示例

当 ngIf 为 false 时构建 Angular4 ng-content

使用ngIf更改Angular 8中的图像时的问题编写语法

Angular:使用 NgIf 删除组件、内部表单和元素?

未捕获的 ReferenceError:ace 未在 Angular 4 中定义