Remove validation in angular 4 when it's not render in NgIf
Asked Answered
N

5

10

I want to remove validation for which control is not rendered by using NgIf. I was try to use directive to remove with hidden control but cannot do the same because it not render in template. So I can not check formControlName with ElementRef in directive. Here is ts file

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

and template

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

I want to remove Validation of text2 dynamically and globally. Not remove validator in ts file.

Normanormal answered 23/4, 2018 at 4:3 Comment(2)
You should put code also, so others can help you rather than just stating problem in text.Sequel
I just put my code in file with edit. Please help me check this outNormanormal
S
6

This Angular source GitHub Issue comment by Kara seems extremely relevant, and illustrates how you might solve the problem by treating the reactive model as "source of truth" and create your ngIf expression off of that source of truth, instead of the reverse. This shows it's by design and you have to make some effort not to mix up template-driven and reactive form ideas.

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.

Structural answered 13/3, 2019 at 20:55 Comment(1)
That was what im looking for. Thank for your help. Cheer!Normanormal
H
4

When the condition property is changed then call the method dynamically to set and remove the validation. for example,

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();
  }
}
Hanako answered 24/4, 2018 at 10:2 Comment(2)
I can change the validator manually. But there are too many control. And for any change request, I need to update the validator of form. My form is too complicated to implement this way. It's about 10+ condition base on value of some control in form.Normanormal
I hope this is the way to set and remove validators to form control dynamically.Hanako
S
1

Since, your formcontrol text2 is dependent on some condition. it should not be as required control. So you reactive form control should be

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

If there is scenario, where you want to ensure that text should be required whenever it's present in dom then use custom validators in angular. Refer documentation of the same for your implementation.

Sequel answered 23/4, 2018 at 5:33 Comment(4)
So I need to remove all validator in component and put required attribute to template? Assume that all my field are required if it's visibleNormanormal
nope, have you followed documentation link shared above for same?Sequel
it's not the same with my case. Text1 and text2 visibility is base on server data. Assume that data return from server is { enableControl: ["text1", "text3", ...]}. How do I implement this with 2 type of button is Submit and Save.Normanormal
Additionally, in some case, visibility of some control is base on another control value. How to I handle my form without not setValidator or remove Validator.Normanormal
K
1

Here the Example: on runtime you can update validators based on checkbox value.you can set field as required and remove also.

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

Keeter answered 23/4, 2018 at 6:53 Comment(1)
for me the simple fact of calling updateValueAndValidity() after the selection changed, solved the issue. I placed the Validators.required on both fields from the beginning, then I am just checking isEmployeeChanged() { this.personForm.controls["myFirstComponent"].updateValueAndValidity(); this.personForm.controls["mySecondComponent"].updateValueAndValidity();} Aviles
T
0

What i did (and work for me), create an alternative formgroupcontrol with another button [disabled], manage the *ngIf for the button and for the form.

<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>
Thirtyone answered 21/2, 2019 at 5:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.