-
Notifications
You must be signed in to change notification settings - Fork 25.7k
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: disabled attribute cannot be set dynamically anymore #11271
Comments
@Krisa If you want to set the disabled state programmatically, you'll need to call export class App {
isDisabled = false
form = new FormGroup({
'first': new FormControl({value: 'hello', disabled: false})
})
constructor() {
setTimeout(() => {
this.form.get('first').disable();
}, 10)
}
} See example here: http://plnkr.co/edit/UXNiM8Pz73Go27nDRD7M?p=preview Re the first example, we deliberately haven't hooked up the template-driven entry point to reactive forms. That's why we throw the warning. If we did hook it up, users would immediately run into 'changed after checked' errors because the validation of reactive forms occurs synchronously. We generally don't suggest mixing reactive and template-driven patterns for this reason. We considered throwing an error instead, but we were concerned that if someone had a custom form component that used |
@kara - I was aware of the Let me take a bit more complicated example than what we can see above. I have a form composed of roughly 70 to 80 fields split across several components. Some part of the form is disabled/enabled, shown/hidden based on either some attributes of the record (on load, or on save), or depending on the user viewing this record, or even from where the record is being called (i.e. different route). I'm playing with the Now, if I have to refactor this code based on what you suggest, I will probably need a central function in my class, and call Either way, I will have to live with what is implemented. I have so far downgraded the form module to 0.3.0, hoping for a PR resolving the situation above, but based on your comment, I fear this spec is not going to change, is it? Could you please (re)confirm one or the other? Thanks |
@kara Thanks for your great work so far! Please reconsider |
If I call
This outputs
Why is this the case? I want the control to be disabled, but the from itself to be still valid. |
@capi Looks like a bug to my opinion, but it's also not related to the original issue I've reported, which is the ability to set the Please open a new issue for that to not mix up things |
@capi I have the same behaviour like you. For me this is wrong.... |
@udos86 @Krisa 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 On the other hand, I was thinking about a custom directive to replace |
Is there any update on this? I mean, now you have a view framework that lets you work with declarative, one-way binding code for a load of attributes and for whole content items, but then for disabled attribute you're stuck to imperative code? |
@kara @pkozlowski-opensource Thank you |
@Krisa, i would recommend to avoid reactive forms. Reactive forms require a lot of coding if want interaction with other parts of template and offer very small benefits. You should be able to create configurable custom validator (or even validate your data model manually) and use it instead of |
@Krisa On our project, we quick-solved this by using two custom utility methods: import { FormGroup, FormControl, AbstractControl } from "@angular/forms";
export function includeInForm(form: FormGroup, controlName: string, includeChildren?: boolean) {
const ctl = form.get(controlName);
makeOptional(ctl, false, includeChildren);
}
export function excludeFromForm(form: FormGroup, controlName: string, excludeChildren?: boolean) {
const ctl = form.get(controlName);
makeOptional(ctl, true, excludeChildren);
}
function makeOptional(ctl: AbstractControl, isOptional: boolean, children?: boolean) {
if (isOptional) {
(<any>ctl).__validator = ctl.validator || (<any>ctl).__validator;
ctl.clearValidators();
} else {
ctl.setValidators(ctl.validator || (<any>ctl).__validator);
}
if (children) {
Object.keys((<FormGroup>ctl).controls).forEach((control) => {
makeOptional(ctl.get(control), isOptional);
});
}
ctl.updateValueAndValidity();
} It might not be the most elegant - for sure - but it works and made the code work again with minimal changes. |
I'm also looking for a simpler and less verbose solution to disable using form variables! |
Why is this closed? There's still no clear way to declaratively disable a form control, nor workaround for it. |
Seems that the angular team doesnt want to communicate on this...max be thés are still discussing alternatives |
Ok, best workaround I could think of is to create a directive to do the dirty job, and "declaretively" test for a boolean condition and get a form/input/FormControl disabled without the need test for expressions that sometimes aren't observables where it's easy to know when something changed (i.e.: to depend on the change detection system). You can even use it to disable other FormControl, or use another FormControl.value as a condition to disable itself.
And it can be used like: |
@andrevmatos |
@raisindetre |
Ah, got you - thanks! I was almost there at one point but I was wrapping the formControlName directive in square brackets. Still trying to get the hang of model/reactive driven forms. |
I updated your directive to what I would consider a bit cleaner of an approach, sharing the formControl input passed from the html element that is already a formConrol.
This allows us to remove the duplication from the template
|
@kara |
@andrevmatos @PTC-JoshuaMatthews we've found also the version at this GH issue comment by @unsafecode to be quite interesting. |
I have been facing the same issue - there isn't a clean way to disable portions of a reactive/model-driven form. The use case is pretty common: Consider a group radio buttons that each display a child form when selected, and each subform that is not selected needs to be disabled in order to make the entire form valid. I really like @PTC-JoshuaMatthews 's |
@kara why is this closed? |
@billfranklin The correct way is to bind to attr.disabled. Check the solution that kekeh posted. |
@kekeh 's solution is great but it does not work with custom form controls. I am trying to conditionally disable form fields in angular2-json-form-schema. I am planning to do it by using a directive the way @PTC-JoshuaMatthews has suggested. Is there any better way to achieve the same? I know I can implement ngDoCheck in custom controls but it won't be an efficient solution. |
any news on this? |
Not sure why this is closed even though no clean solution/approach was given ... |
@mkotsollaris Did you read the reply with the most thumbs up? |
I ended up abandoning this approach altogether in my applications. Instead of setting isDisabled property on my component and expecting the form to track that properties changes, I instead call this.form.controls[“mycontrol”].disable() in the ngAfterViewInit hook. If the status changes I explicitly call enable() to reactive it. This approach works fine as I’m already responding to some event to enable/disable the control so no problem doing it directly through the form api as opposed to attempting to bind the disabled status to a property. I suspect my desire to bind it comes from my angular 1 days. |
Is it good/correct way of using below code for disabling the forms ??? <input [attr.disabled]="disabled?'':null"/> I have tried with the suggestion provided by angular, form = new FormGroup({
firstName: new FormControl({value: 'Vishal', disabled: true}, Validators.required),
lastName: new FormControl('Drew', Validators.required)
}); Then, in html <input type="text" formControlName="firstName"
[value]="form.controls['firstName'].value"
[disabled]="form.controls['firstName'].disabled"> |
@Highspeed7 That works "partially" in that the element's @kekeh Added to this solution by binding the ternary So on the template, this would be the workaround: |
What are the implications of setting |
@sgronblo afaik there aren't any. I was under the impression that |
It worked for me even by just having this.disabled = false; etc, wherever I needed it. The functions weren't necessary. |
this.myForm.controls['id'].disable(); |
Is there a more proper solution for this case instead of binding to the disabled attribute (which is kept after the form is re-enabled) ? |
@danzrou Does the answer directly above you not work for you? Just disable the form control that the input is tied to and the input will be dealt with automatically. |
@danzrou yes, fwiw, afaik that is the defacto way to enable/disable form controls: Keep in mind that reactive forms are not the standard form set. They are intended to be used programmatically, not through the dom, which is why (I suspect) they don't allow the bindings anymore. |
My current situation is that I am passing a form control to a child component and the control is changing dynamically according to user selection. If I pass a control which has disabled=true, the input element is indeed disabled. Then I pass a a new control which has disabled=false (and I made sure it is not disabled) and yet the disabled attribute remains on the input field. This still happens in Angular 7.2.3 |
@danzrou can you elaborate on how are you passing the form control into the child component? My guess is that the child component isn’t seeing the new form control. |
@PTC-JoshuaMatthews it passes it using standard input. Some more details:
|
this.myForm.controls['id'].enable(); |
@danzrou I could see why that wouldn’t work, you’re swapping out the form control but not changing disabled state while it is bound to the input. Form controls aren’t really meant to be swapped out like that, there’s no event taking place for it to react to. If you’re sure that you have to swap form controls, I would recommend using an input setter to detect when the form control changes and re-initializing the input when this happens. https://stackoverflow.com/questions/36653678/angular2-input-to-a-property-with-get-set As for how to reinitialize it a dirty solution would be by hiding it with an ngIf and then showing it in a timeout directly after. A cleaner solution might just be to call formControl.setDisabled(formControl.disabled). Just as long as you’re doing this while the form control is bound to the input. Might have to get creative with a timeout |
please provide a flag in angular config to disable this warning |
@sgentile I think the entire point of the warning is that you're using reactive forms, and therefore should not be using template-driven-form syntax to control form state. This could cause a lot of confusion when a developer sees reactive forms, tries to program in reactive forms, and unknowingly ends up fighting a template-driven form convention because it was implemented in conflict. The solution is to set/unset the disabled state in code just like you're handing the rest of your reactive form state ( |
Quite a few assumptions here.
For example, I'm using Primeng and I need to pass disabled to their control
and this causes the console warning.
I think it's fine to show console warnings to developers, however they
should be able to decide if they want them. Make a setting in the
angular.json file
It's over extending imo.
…On Sat, Aug 31, 2019, 8:06 PM Layton Miller ***@***.***> wrote:
@sgentile </~https://github.com/sgentile> I think the entire point of the
warning is that you're using reactive forms, and therefore should not be
using template-driven-form syntax to control form state. This could cause a
lot of confusion when a developer sees reactive forms, tries to program in
reactive forms, and unknowingly ends up fighting a template-driven form
convention because it was implemented in conflict.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#11271?email_source=notifications&email_token=AABAKNGBGNDK3TLWNHCXVJLQHMBPTA5CNFSM4CON24SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5TXM7Y#issuecomment-526874239>,
or mute the thread
</~https://github.com/notifications/unsubscribe-auth/AABAKNE74ZR3UZBVBEQQXHDQHMBPTANCNFSM4CON24SA>
.
|
Furthermore when I did attempt to use disable it didn't work
…On Sat, Aug 31, 2019, 8:06 PM Layton Miller ***@***.***> wrote:
@sgentile </~https://github.com/sgentile> I think the entire point of the
warning is that you're using reactive forms, and therefore should not be
using template-driven-form syntax to control form state. This could cause a
lot of confusion when a developer sees reactive forms, tries to program in
reactive forms, and unknowingly ends up fighting a template-driven form
convention because it was implemented in conflict.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#11271?email_source=notifications&email_token=AABAKNGBGNDK3TLWNHCXVJLQHMBPTA5CNFSM4CON24SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5TXM7Y#issuecomment-526874239>,
or mute the thread
</~https://github.com/notifications/unsubscribe-auth/AABAKNE74ZR3UZBVBEQQXHDQHMBPTANCNFSM4CON24SA>
.
|
@sgentile Yeah sorry - wasn't attempting to assume but without more info you sound like most developers who complain about a feature w/o a justification. Framework and language devs have to make the most generic choices possible and a lot of times that gets overlooked. I think your case is a valid point. Ideally you'd submit a feature request for this since this is a closed ticket that is attempting to report a bug. |
You have motivated me to see if I can get a ViewChild reference to the
control and see if I can set it programatically
…On Sat, Aug 31, 2019, 8:18 PM Layton Miller ***@***.***> wrote:
@sgentile </~https://github.com/sgentile> Yeah sorry - wasn't attempting to
assume but without more info you sound like most developers who complain
about a feature w/o a justification. Framework and language devs have to
make the most generic choices possible and a lot of times that gets
overlooked. I think your case is a valid point.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#11271?email_source=notifications&email_token=AABAKNEXR32GSL3U5ZS6TZLQHMC5LA5CNFSM4CON24SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5TXSJI#issuecomment-526874917>,
or mute the thread
</~https://github.com/notifications/unsubscribe-auth/AABAKND5MXVOV5BYO4R6DS3QHMC5LANCNFSM4CON24SA>
.
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
I'm submitting a bug/feature request (check one with "x")
Current behavior
Have a look at that, (demo: http://plnkr.co/edit/P25dYPC5ChRxpyxpL0Lj?p=preview):
There is a warning message asking to refactor the code, but more importantly, the input is unfortunately not disabled. Refactoring the code to what is suggested is not helping either, i.e. doing this will not work, (demo: http://plnkr.co/edit/Gf7FGR42UXkBh6e75cm2?p=preview):
Expected/desired behavior
After the callback of the setTimeout is executed, the input should be disabled, but it is not.
Reproduction of the problem
Yes, please look here: http://plnkr.co/edit/P25dYPC5ChRxpyxpL0Lj?p=preview
What is the motivation / use case for changing the behavior?
To have the same behaviour, or similar one than in RC5. At least, we shall have a possibility, even if it's a breaking change, to set dynamically the
disabled
attribute. As of now, it does not seem possible anymore.Please tell us about your environment:
The text was updated successfully, but these errors were encountered: