angularangular12angular-reactive-forms

Reactive forms - Warning when using the disabled attribute


I want to disable a select box in one of my forms while there is nothing to select in it until the data for this select box was returned from the server. I need to do this because the data shown in the select box depends on what was selected in another select box. Whenever something is selected in that other select box, I need to load the corresponding options from the server. To disable the select box while there is no data available for it, I am using the disabled attribute like this:

<mat-select formControlName="formId" [disabled]="formNames.length === 0">
  <mat-option
    *ngFor="let formName of formNames"
    [value]="formName.id"
  >
    {{ formName.formName }}
  </mat-option>
</mat-select>

I then subscribed to the valueChanges event of the other select box like this:

this.createForm.controls.formTypeId.valueChanges.subscribe((value: number) => {
  this.formsService.getFormNames(value).subscribe((formNames) => {
    this.formNames = formNames;
  });
});

While this seems to work perfectly fine, I keep getting the following warning in the browser console:

  It looks like you're using the disabled attribute with a reactive form directive. If you set disabled to true
  when you set up this control in your component class, the disabled attribute will actually be set in the DOM for
  you. We recommend using this approach to avoid 'changed after checked' errors.

  Example:
  form = new FormGroup({
    first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
    last: new FormControl('Drew', Validators.required)
  });

I do not understand what this warning means and the suggested solution also doesn't really help me because I do not simply want to set the disabled property to true. What am I supposed to actually do to achieve the desired behavior of disabling the select box while there are no form names available? Or is my approach fine and I should just ignore this warning?


Solution

  • Angular is telling you that you are mixing two different types of approaches to forms. One is template-driven, while the other is reactive forms. As you are using reactive forms angular warning you that you should use the reactive approach to disable your form control instead of using the HTML attribute [disabled]

    In your use case you can just enable or disable the field depending upon it's length in the subscribe block which should look like this:

    this.createForm.controls.formTypeId.valueChanges.subscribe((value: number) => {
      this.formsService.getFormNames(value).subscribe((formNames) => {
        if(formNames.length > 0){
            this.createForm.controls.formControlName.enable();
        } else {
            this.createForm.controls.formControlName.disable()
        }
        this.formNames = formNames;
      });
    });
    

    Read this for better insight: https://indepth.dev/posts/1474/disable-form-controls-using-forms-api-or-html-attributes

    Also, you are using nested subscribes. i.e. one subscribe in another subscribe, which is a bad practice and should be avoided. I advise you to look into RxJs maps such as switchMap, mergeMap etc whichever fulfills your use-case. For example, if you want to cancel the previous subscription when a new event comes you can use switchMap:

    this.createForm.controls.formTypeId.valueChanges
          .pipe(
              switchMap((value: number) => {
                  return this.formsService.getFormNames(value);
              })
          )
          .subscribe(formNames => {
              if (formNames.length > 0) {
                  this.createForm.controls.formControlName.enable();
              } else {
                  this.createForm.controls.formControlName.disable();
              }
              this.formNames = formNames;
          });