Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(forms): add support for disabled controls #10994

Merged
merged 1 commit into from
Aug 24, 2016
Merged

Conversation

kara
Copy link
Contributor

@kara kara commented Aug 22, 2016

In HTML5 forms, controls marked as disabled are not considered when calculating the validity or serialized value of the parent form. However in Angular's current forms API, disabled controls still behave like normal controls and are included in all value/validity checks.

This is confusing because it differs from the HTML5 spec, but it's also confusing because it creates situations where a disabled control can make the whole form invalid, and worse, it's not editable in the UI so this situation cannot be remedied without re-enabling the control.

Changes:

We are adding a new status to controls called "DISABLED". A control can either be:

VALID: control has passed all validation checks
INVALID: control has failed a validation check
PENDING: control is in the midst of conducting a validation check
DISABLED: control is exempt from validation checks

These statuses are mutually exclusive. We are also adding disabled and enabled getters to AbstractControls.

When you disable a control, it's not included in parent validation or value serialization. So if your disabled control is invalid, the parent form can still be valid if it has other valid controls. Group values will also omit the values of disabled controls. Group status is always reduced from the statuses of its children, so if all a group's children are disabled, the group is disabled.

Reactive forms

Using reactive forms, form controls are disabled in the component class. When you disable a control, the disabled attribute is added for you in the DOM (so no need to do this yourself).

You have a few choices for how to disable a control. If you want the control to be disabled from the start, you can pass a boxed value as the first arg when you instantiate your FormControl. This boxed value contains all the form state that cannot be calculated (as validation state, dirtiness, and touched are all derived).

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

Note that if you don't care to set disabled state, you can just pass in a value without the wrapper object like before.

You can also imperatively enable or disable controls with the enable() and disable() methods:

this.form.disable();                   // disables itself and all children
this.form.get('first').disable();      // disables just this control

Angular 1 style (template-driven) forms

To disable a control in Angular-1 style forms, just add the disabled attribute or bind to the disabled property. The control will be disabled for you by ngModel under the hood.

<form>
   <input name="first" ngModel>
   <input name="last" ngModel required [disabled]="isDisabled">
</form>

Fixes #4460.

Breaking change: this PR also removes the deprecated optionals API, which has significant overlap with the new API. For more information about that and the changes in this PR, there's more info in the proposal doc.

@kara kara force-pushed the disabled-pr branch 4 times, most recently from 3b12acb to 10c46f3 Compare August 22, 2016 22:36
@kara kara added action: review The PR is still awaiting reviews from at least one requested reviewer and removed state: WIP labels Aug 22, 2016
@vsavkin
Copy link
Contributor

vsavkin commented Aug 23, 2016

I don't think you need to use StringMapWrapper.

@vsavkin
Copy link
Contributor

vsavkin commented Aug 23, 2016

Am I right to assume that because 'reset' does not change controls' statuses we won't have any issues regarding atomicity (e.g., the number of emitted events)?

@vsavkin
Copy link
Contributor

vsavkin commented Aug 23, 2016

So controls can only be disabled by Angular. They cannot disable themselves. Is it correct?

@kara
Copy link
Contributor Author

kara commented Aug 23, 2016

  1. Using that because it's faster than Object.keys, but can switch if you prefer
  2. Because emitEvent == false on reset, disable/enable methods don't emit an event in that case, only reset.
  3. Controls can only be disabled using our methods or by adding a disabled attribute (which we then handle as well). The property is a getter that cannot be directly modified by a user.

@kara kara force-pushed the disabled-pr branch 5 times, most recently from 107da5a to 881e3ad Compare August 23, 2016 19:20
@bradlygreen bradlygreen added this to the 2.0.0-rc.6 milestone Aug 24, 2016
@@ -175,6 +185,39 @@ export abstract class AbstractControl {
}
}

disable({onlySelf, emitEvent}: {onlySelf?: boolean, emitEvent?: boolean} = {}): void {
emitEvent = isPresent(emitEvent) ? emitEvent : true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we do not compile to Dart anymore, you should be able to use the default value

Copy link
Contributor Author

@kara kara Aug 24, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit: we realized that it needs to be this way to ensure something like {onlySelf: true} doesn't set emitEvent to false.

@vsavkin vsavkin added pr_state: LGTM and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Aug 24, 2016
@kara kara added action: merge The PR is ready for merge by the caretaker state: blocked and removed action: merge The PR is ready for merge by the caretaker labels Aug 24, 2016
@kara kara added action: merge The PR is ready for merge by the caretaker and removed state: blocked labels Aug 24, 2016
@vicb vicb merged commit 2b313e4 into angular:master Aug 24, 2016
@marcalj
Copy link

marcalj commented Sep 1, 2016

How we should set a conditional disabled form control using Reactive Forms to simulate [disabled]="condition" ? Thanks!

@0cv
Copy link

0cv commented Sep 1, 2016

Wondering the same. My natural feeling was to do something like that:
new FormControl({disabled: this.Variable}) but it's not doing it

@kara
Copy link
Contributor Author

kara commented Sep 1, 2016

@marcalj @Krisa You need to pass in the full boxed value, with both value and disabled.

new FormControl({value: '', disabled: true})

Looking only for the disabled property is too generic and we could end up eating form control values that happen to have it. For that reason, we require setting exactly those two properties.

@0cv
Copy link

0cv commented Sep 1, 2016

Thanks @kara for the super quick answer. My question was at least how to bind a dynamic disabled. In RC5, it was possible, like @marcalj shown before to do something like:
[disabled]="isDisabled && isAnotherVariable || isYetSomethingElse" and if that evaluates to true, then it's dynamically disabled. Follow-up issue created here: #11271

While this is eventually something else, your specific example seems to fail, I've reported just before this issue:

#11253

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 14, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: forms cla: yes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ngform disable validation when control disabled
7 participants