当 angular 4 未在 NgIf 中呈现时移除验证
Remove validation in angular 4 when it's not render in NgIf
我想删除未使用 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 文件中的验证器。
因为,您的 formcontrol text2 取决于某些条件。它不应该按要求控制。所以你的反应形式控制应该是
this.form = this._fb.group({
text1: ['', Validators.required],
text2: ['',]
});
如果在某些情况下,您希望确保文本在 dom 中出现时应该是必需的,那么请在 angular 中使用自定义验证器。请参考 documentation 的实现。
Here
示例:在运行时,您可以根据复选框更新验证器 value.you 可以根据需要设置字段,也可以删除。
当条件 属性 改变时,动态调用该方法来设置和删除验证。例如,
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();
}
}
我所做的(并为我工作),使用另一个按钮 [禁用] 创建一个替代的 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>
Kara 的 Angular 来源 GitHub 问题评论似乎非常相关,并说明了如何通过将反应模型视为 "source of truth" 并创建您的 ngIf
表达出真相的来源,而不是相反。这表明它是设计使然,您必须努力避免混淆模板驱动和反应式表单的想法。
https://github.com/angular/angular/issues/7970#issuecomment-228624899
Thanks for taking the time to describe the problem. I took a look at
your example code, and it seems that you are using the reactive form
directives (a.k.a "model-driven" directives: ngFormModel, etc), but a
template-driven strategy. The fact that the ngIf does not remove the
control from the form's serialization and validation is actually by
design, and here's why.
In each form paradigm - template-driven and reactive - there can only
be one source of truth for the list of active controls. In the
template-driven paradigm, the source of truth is the template. In the
reactive equivalent, the source of truth is the form model created in
the parent component. The DOM does not dictate the state of your form.
For this reason, if you remove form control elements from the DOM
while using a reactive approach, the form controls are not necessarily
changed in the source of truth unless you want them to be. You can
choose to update the controls imperatively by calling
this.form.removeControl('controlName'), or you can choose to keep the
controls in your form. This flexibility allows you to add or remove
inputs from the DOM temporarily while keeping their form values
serialized (e.g. if you have a number of collapsible sections to your
form, you can remove sections on collapse without impacting the value
of your form). We don't want to restrict this flexibility and
complicate ownership by forcing the model to always match the DOM.
So in your case, if you choose a reactive strategy, you'll want to
invert your logic to rely on the source of truth - the model.
Specifically, this means removing the control imperatively in the
model by calling this.form.removeControl('name') when the button is
clicked. Then, the ngIf should depend on the control's presence in the
model with *ngIf="form.contains('name')", rather than the other way
around. See example plunker here:
http://plnkr.co/edit/V7bCFLSIEKTuxU9jcp6v?p=preview
It's worth noting that if you're still using beta.14 (as in your
plunker), you'll need to call this.form.updateValueAndValidity()
manually. This requirement was removed in #9097, so versions after
RC.2 don't require the call.
Another option is to convert to a template-driven strategy (no
ngFormModel), which will remove the control from the form when it's
destroyed from the template. Example:
http://plnkr.co/edit/s9QWy9T8azQoTZKdm7uI?p=preview
I'm going to close this issue as it works as intended, but I think we
could make the experience a lot friendlier. A good start would be some
more cookbooks and guides in the documentation.
我想删除未使用 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 文件中的验证器。
因为,您的 formcontrol text2 取决于某些条件。它不应该按要求控制。所以你的反应形式控制应该是
this.form = this._fb.group({
text1: ['', Validators.required],
text2: ['',]
});
如果在某些情况下,您希望确保文本在 dom 中出现时应该是必需的,那么请在 angular 中使用自定义验证器。请参考 documentation 的实现。
Here
示例:在运行时,您可以根据复选框更新验证器 value.you 可以根据需要设置字段,也可以删除。
当条件 属性 改变时,动态调用该方法来设置和删除验证。例如,
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();
}
}
我所做的(并为我工作),使用另一个按钮 [禁用] 创建一个替代的 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>
Kara 的 Angular 来源 GitHub 问题评论似乎非常相关,并说明了如何通过将反应模型视为 "source of truth" 并创建您的 ngIf
表达出真相的来源,而不是相反。这表明它是设计使然,您必须努力避免混淆模板驱动和反应式表单的想法。
https://github.com/angular/angular/issues/7970#issuecomment-228624899
Thanks for taking the time to describe the problem. I took a look at your example code, and it seems that you are using the reactive form directives (a.k.a "model-driven" directives: ngFormModel, etc), but a template-driven strategy. The fact that the ngIf does not remove the control from the form's serialization and validation is actually by design, and here's why.
In each form paradigm - template-driven and reactive - there can only be one source of truth for the list of active controls. In the template-driven paradigm, the source of truth is the template. In the reactive equivalent, the source of truth is the form model created in the parent component. The DOM does not dictate the state of your form. For this reason, if you remove form control elements from the DOM while using a reactive approach, the form controls are not necessarily changed in the source of truth unless you want them to be. You can choose to update the controls imperatively by calling this.form.removeControl('controlName'), or you can choose to keep the controls in your form. This flexibility allows you to add or remove inputs from the DOM temporarily while keeping their form values serialized (e.g. if you have a number of collapsible sections to your form, you can remove sections on collapse without impacting the value of your form). We don't want to restrict this flexibility and complicate ownership by forcing the model to always match the DOM.
So in your case, if you choose a reactive strategy, you'll want to invert your logic to rely on the source of truth - the model. Specifically, this means removing the control imperatively in the model by calling this.form.removeControl('name') when the button is clicked. Then, the ngIf should depend on the control's presence in the model with *ngIf="form.contains('name')", rather than the other way around. See example plunker here: http://plnkr.co/edit/V7bCFLSIEKTuxU9jcp6v?p=preview
It's worth noting that if you're still using beta.14 (as in your plunker), you'll need to call this.form.updateValueAndValidity() manually. This requirement was removed in #9097, so versions after RC.2 don't require the call.
Another option is to convert to a template-driven strategy (no ngFormModel), which will remove the control from the form when it's destroyed from the template. Example: http://plnkr.co/edit/s9QWy9T8azQoTZKdm7uI?p=preview
I'm going to close this issue as it works as intended, but I think we could make the experience a lot friendlier. A good start would be some more cookbooks and guides in the documentation.