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

RC6 Forms: Calling disabled() on FormControl makes entire form invalid #11379

Closed
capi opened this issue Sep 6, 2016 · 10 comments
Closed

RC6 Forms: Calling disabled() on FormControl makes entire form invalid #11379

capi opened this issue Sep 6, 2016 · 10 comments

Comments

@capi
Copy link

capi commented Sep 6, 2016

I'm submitting a ... (check one with "x")

[X] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior
If I call this.myForm.controls["code"].disable();, this.myForm.valid is always false:

this.myForm.patchValue({code: 17});
console.log(this.myForm.valid);
this.myForm.controls["code"].disable();
console.log(this.myForm.valid);

This outputs

true
false

Expected/desired behavior
The form should still be valid, even though the control has been disabled.

Reproduction of the problem
http://plnkr.co/edit/B8cA0zzcrt4SjxrGMlRf?p=preview

  • Angular version: 2.0.0-rc.6
  • Browser: [Chrome 53.0.2785.92 | Firefox 48 ]
  • Language: [TypeScript 1.8]
  • Remarks

I initially remarked this as a comment in #11271, but I don't want to hijack the other issue, which is about dynamically binding [disabled].
It could be that PR #11257 fixes this already, but I am not sure, so I decided to file this bug-report.

@kara
Copy link
Contributor

kara commented Sep 7, 2016

[copied from #11257]

@capi The way the model works is that a parent form group is disabled if all its child controls are disabled. The status of group controls is always calculated as a reduction of the child controls.

In your example, your form only has one form control. Once you disable it, you are effectively disabling the whole form because all its (one) children are disabled. If you log the status of the form, you'll see that its status is 'DISABLED', which is why valid is false. The statuses are mutually exclusive. Closing, as this works as designed.

@kara kara closed this as completed Sep 7, 2016
@capi
Copy link
Author

capi commented Sep 8, 2016

Thanks, that was a misconception of my part then. So a work-around for one-element forms is to add a dummy-control?

@kara
Copy link
Contributor

kara commented Sep 8, 2016

@capi Could you tell me more about your use case? You have a one-control form that is disabled, but the status needs to be valid for some reason?

@capi
Copy link
Author

capi commented Sep 8, 2016

@kara It's a special use-case. It's about interacting with a barcode scanner from inside an Android WebView. The user can enter the ID manually, or if they scan it, the value is forced and the value of the field is fixed. The submit button is bound to myForm.valid (and can be used for re-loading the data). At the moment I work around it by modifying the check to [disabled]="!forcedValue && !myForm.valid".

@kara
Copy link
Contributor

kara commented Sep 8, 2016

Thanks for the additional info. Am I understanding correctly that you'd like the field to be readonly if it is scanned in? Have you considered binding to the readonly property rather than disabling the input? Form controls that are read-only can still be valid.

Alternatively, you could disable the control if it's explicitly invalid - [disabled]="myForm.invalid". That way, it's only disabled if the status === 'INVALID'. VALID or DISABLED would both be fine.

Also worth noting that if you have only one control, you might consider switching to using formControl rather than formGroup.

@marcalj
Copy link

marcalj commented Sep 13, 2016

@kara Just to make it clear. While using ReactiveForms, to have a dynamic disabled (binded to custom condition changing at runtime by the user input) we have to put code in the template.
To have dynamic required (binded to custom condition changing at runtime by the user input) we have to put code in the FormGroup/FormControl ?

Thanks.

@unsafecode
Copy link

@kara @marcalj We are experiencing the same behavior, and it looks quite troublesome to solve: on one hand, we could code the dynamic enable/disable behavior by subscribing to valueChangesof FormControl, but it requires lot of code each time (and we have large forms with many fields).

On the other hand, I was thinking about a custom directive to replace disable, taking the FormControland an Observable, in order to keep the reactive approach in templates, but I need to experiment with it.

@unsafecode
Copy link

I've just stubbed this custom directive, which should behave as desidered. Warning: it's just a proof-of-concept, not a complete solution. Feedbacks are welcome, of course.

@Directive({
  selector: '[formControlName][dynamicDisable]'
})
export class DynamicDisable implements OnInit, OnChanges {
  constructor(
    @Optional() @Host() @SkipSelf() private parent: ControlContainer,
  ) { 

  }

  @Input() formControlName: string;  
  @Input() dynamicDisable: boolean;

  private ctrl: AbstractControl;

  ngOnInit() { 
    if(this.parent && this.parent["form"]) {
      this.ctrl = (<FormGroup>this.parent["form"]).get(this.formControlName);
    }
  }

  ngOnChanges() {
    if (!this.ctrl) return;

    if (this.dynamicDisable) {
      this.ctrl.disable();
    }
    else {
      this.ctrl.enable();
    }
  }
}
<form [formGroup]="form">
    <label>
        Make required
        <input type="checkbox" formControlName="isRequired" [(ngModel)]="isRequired" />
    </label>
    <p>
        <input type="text" formControlName="yourName" [(ngModel)]="yourName" [dynamicDisable]="!isRequired" />
    </p>
    <p>
        Valid: {{form.valid}}
    </p>
</form>
@Component({
  selector: 'home',
  styleUrls: ['./home.css'],
  templateUrl: './home.html'
})
export class Home {

  form: FormGroup;
  isRequired: boolean = true;
  yourName: string = '';

  ngOnInit() {
    this.form = new FormGroup({
      "isRequired": new FormControl(true),
      "yourName": new FormControl('', [])
    });

@jamesthurley
Copy link

@unsafecode thanks, your directive is working well in my project and saved me some refactoring effort.

@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 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants