angular: FormGroup.disable() and FormGroup.enable() do not allow resetting disabled state.

I’m submitting a…


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior

When dealing with an ngForm, I want to disable all included controls so users cannot modify them, e.g. during submit.

<form (ngSubmit)="onSubmit()" #form="ngForm">
   <input [disabled]="disabled">
   <input disabled="true"> <!-- always disabled -->
</form>

I can access the above form from within the component controller to overwrite the formGroup states:

this.form.form.disable();
this.form.form.enable();

When doing this, first all controls get disabled, then all get enabled. Even the second form which has disabled="true" hard-coded gets enabled and there is no way to get it back to its self-governing state.

Expected behavior

There could be multiple solutions to this:

  • the most specific attribute ([disabled] on the input) could take precedence.
  • a “force” option in disable() and enable()
  • a method on AbstractControl to reset the disabled state.

Minimal reproduction of the problem with instructions

http://plnkr.co/edit/M71eHXdG8DcNJOYZDuhe The plnkr has two inputs: one “regular” and one is always disabled. Click the submit button -> both inputs get disabled. After the timeout -> both inputs get enabled. The “always disabled” input cannot be reset.

What is the motivation / use case for changing the behavior?

If we can revert to the input disabled value, we can build forms that enforce a state during a certain process (e.g. async update) and then reset the disabled states after the async operation is completed.

Environment

Angular version: 4.4.1

Browser:

  • Chrome (desktop) version XX
  • Chrome (Android) version XX
  • Chrome (iOS) version XX
  • Firefox version XX
  • Safari (desktop) version XX
  • Safari (iOS) version XX
  • IE version XX
  • Edge version XX

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 34
  • Comments: 18 (1 by maintainers)

Most upvoted comments

this is a usable workaround: https://stackoverflow.com/questions/40494968/reactive-forms-disabled-attribute in your html: <formly-form [attr.disabled]=“formsDisabledVariable”>

in your .ts file: formsDisabledVariable: boolean = false;

and then conditional disable with: this.formsDisabledVariable = true;

re-enable with: this.formsDisabledVariable = null; // nulled not false

Any update on the issue? I also need to disable the entire form and then enable it back to initial state.

I too have a form that enables/disables FormControls based on the value of other FormControls (seems like a pretty common use case). While a working enable/disable({onlySelf: true}) would be ideal, just setting the value of the parent FormGroup to its existing value fixes the enabled/disabled state of the logic controlled FormControls.

Something like:

this.formGroup.enable({ onlySelf: true, emitEvent: false }); // Hopefully will work one day
this.formGroup.setValue(this.formGroup.value); // Workaround

this works… sorry for the workaround, but i need it now x.X

          ` setTimeout(() => {
		this.formGroup.disable();
	}, 100);`

Here is my workaround. It’s in typescript, but it’s vanilla. They’re not elegant, but they work.

I made these 2 functions:

export function mapDisabledState(form: FormGroup | FormArray) {
  const result = { rootFormDisabledStatus: form.disabled };
  Object.keys(form.controls).forEach(key => {
    const abstractControl = form.controls[key];
    if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
      result[key] = mapDisabledState(abstractControl);
    } else {
      result[key] = abstractControl.disabled;
    }
  });
  return result;
}

export function restoreFormDisabledState(form: FormGroup | FormArray, keymap: { [pathKey: string]: any }) {
  Object.keys(keymap).forEach(function (key) {
    if (key === 'rootFormDisabledStatus') {
      if (keymap[key]) {
        form.disable({ onlySelf: true, emitEvent: false });
      }
    } else {
      const abstractControl: FormGroup = form.controls[key];
      const result = keymap[key];
      if (typeof result === 'object') {
        restoreFormDisabledState(abstractControl, keymap[key]);
      } else if (result) {
        abstractControl.disable({ onlySelf: true, emitEvent: false });
      }
    }
  });
}

Then right before form submission I do this so the user can’t touch it while it’s sending:

        this.disabledControlsState = mapDisabledState(this.detailsForm);
        this.detailsForm.disable();

and then after the async call completes I do this

        this.detailsForm.enable();
        restoreFormDisabledState(this.detailsForm, this.disabledControlsState);

I could probably have skipped the last enable and just had the form only re-enable controls that were false but it worked so I moved on.

any news on this? I have the issue on custom form controls, so I can’t add fieldsets everywhere… I’ve just upgraded to v 7.2.16 but still not working with

this._disabled ? this.groupCtrl.disable({onlySelf: !0, emitEvent: !1}) : this.groupCtrl.enable({onlySelf: !0, emitEvent: !1});