Skip to content

Bug: setting [disabled] attribute no longer works with formControlName #48350

Open

Description

@leandrotassi

Command

other

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

Angular 14

Description

When upgrading from Angular 14 to Angular 15 the [Disabled] directive of form controls stopped working.

Is this a bug or did something change?

I'm using version 15.0.2

Minimal Reproduction

<button type="button" id="btnAddPeopleEditor" class="btn btn-success btn-sm" (click)="addPeople(1)"
[disabled]="disabledEditEditor || (habilitarControle(isReadOnly) == 'true')">
Add

Exception or Error

No error occurs. It simply stopped working.

Your Environment

Angular CLI: 15.0.2
Node: 16.17.0
Package Manager: npm 8.18.0
OS: win32 x64

Angular: 15.0.2
... animations, cli, common, compiler, compiler-cli, core, forms
... platform-browser, platform-browser-dynamic, platform-server
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1500.2
@angular-devkit/build-angular   15.0.2
@angular-devkit/core            15.0.2
@angular-devkit/schematics      15.0.2
@angular/cdk                    15.0.1
@angular/material               15.0.1
@nguniversal/builders           15.0.0
@nguniversal/common             15.0.0
@nguniversal/express-engine     15.0.0
@schematics/angular             15.0.2
rxjs                            7.5.7
typescript                      4.8.4

Anything else relevant?

No response

Activity

  1. jnizet commented on Dec 4, 2022

    @jnizet
    Contributor

    Hi @leandrotassi

    I can't reproduce: https://stackblitz.com/edit/angular-ivy-dqrsbv?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.component.html

    It's most probably a bug in your code. You should ask on stackoverflow or on the Angular discord, and provide a complete minimal reproduction.

  2. leandrotassi commented on Dec 4, 2022

    @leandrotassi
    Author

    Hi @jnizet

    the bug occurs when using the formControlName within the formGroup.

    You can look in the 'profile-editor.component.html' component and compare two controls:

    1. <input [disabled]="true" id="street" type="text" formControlName="street" />

    Does not work.

    <input [disabled]="true" id="text_disable1" type="text"/>

    It works

    Here's an example:

    https://stackblitz.com/edit/angular-cu6mce?file=src/app/profile-editor/profile-editor.component.html

  3. jnizet commented on Dec 4, 2022

    @jnizet
    Contributor

    You're not supposed to use disabled when binding to a form control. Look at the browser console: a message is telling you that. See https://stackblitz.com/edit/angular-ivy-dqrsbv?file=src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fapp.module.ts,src%2Fapp%2Fapp.component.html for example. Open the console, and you'll see a big warning message saying:

    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:
    // Specify the disabled property at control creation time:
    form = new FormGroup({
    first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
    last: new FormControl('Drew', Validators.required)
    });

    // Controls can also be enabled/disabled after creation:
    form.get('first')?.enable();
    form.get('last')?.disable();

    And that warning exists for a long time, well before Angular 15.

  4. CynanX commented on Dec 5, 2022

    @CynanX

    The attr.disabled directive has also stopped working, to which there has been no warning in Angular about not using - is this the same, in that it is not supposed to be supported by Angular 15 now? Thanks.

  5. leandrotassi commented on Dec 5, 2022

    @leandrotassi
    Author

    That [Disabled] directive actually stopped working in version 15 and I don't even get the warning in the browser console.
    It was working perfectly in version 14.
    But, anyway, I changed the approach to disable the controls at form creation time, as recommended by Angular.

  6. added this to the needsTriage milestone on Dec 6, 2022
  7. dylhunn commented on Dec 9, 2022

    @dylhunn
    Contributor

    I'm trying to reproduce the regression so I can figure out why this changed. However, it seems to behave exactly the same (odd) way in Angular 14: https://stackblitz.com/edit/angular-ivy-4ymuur?file=src/app/app.component.html

    @leandrotassi , can you help me show it working in Angular 14?

  8. e-oz commented on Dec 22, 2022

    @e-oz

    [attr.disabled] and [disabled] doesn't add "disabled" attribute anymore.
    Quite important change, actually, please mention such changes as "breaking".

  9. e-oz commented on Dec 22, 2022

    @e-oz

    <input [disabled]="true" id="text_disable1" type="text"/>
    It works

    Doesn't work in 15.1.0-next.3 - it only adds ng-reflect-is-disabled="true" to the element in DOM.

  10. e-oz commented on Dec 22, 2022

    @e-oz

    You're not supposed to use disabled when binding to a form control

    @jnizet calling control.disable() doesn't work either.

  11. e-oz commented on Dec 22, 2022

    @e-oz
  12. leandrotassi commented on Dec 22, 2022

    @leandrotassi
    Author

    Here is StackBlitz with reproduction: https://stackblitz.com/edit/angular-ivy-jcwird?file=src/app/app.component.html

    Thanks. At the moment I have no way to test it. As soon as it works, I return the result.

  13. 39 remaining items

  14. PhiLhoSoft commented on Aug 21, 2023

    @PhiLhoSoft

    I have a different use case, and I confirm that when we call formControl.disable(), the setDisabledState function is no longer called in reactive forms.

    We don't use [disabled] nor [attr.disabled] in our code, but the change affected us, in a way that doesn't seem right.
    The .withConfig({ callSetDisabledState: 'whenDisabledForLegacyCode' }) works, but it means we have to do in on each of our declaration, which is ugly and inconvenient.

    The use case, quite common: we have custom form components, for checkbox, radio, text field and so on.
    These components declare NG_VALUE_ACCESSOR and implement ControlValueAccessor and use setDisabledState to set the disabled attribute on the native element. I know there are better ways, but that's legacy code that worked well for years.
    If we disable the form group where the controls are, it works. If we disable individual components, it no longer works.

    [EDIT] I tried to reproduce our problem, and I found out that my above statements are not entirely true…
    My StackBlitz: https://stackblitz.com/edit/angular-xdlrrk?file=src%2Fmain.ts,src%2Fcustom-input%2Fcustom-text-input.component.ts,src%2Fcustom-input%2Fcustom-text-input.module.ts
    Check the ngOnInit part of main.ts (similar issue if the code is in the constructor).
    If I uncomment the line 35 to disable the component (initial state), it fails and the toggle doesn't work right: the form control is disabled, but setDisabledState hasn't been called and the input state doesn't reflect the control state.
    If instead I uncomment lines 37-39, it works, and that's the workaround I use in our application, instead of changing the library creating these custom components (which is our, but I don't like this hack).

    Maybe I missed something in the long thread above, but is it the expected new behavior? Impossibility to start a custom form component in the disabled state? Or did I miss an official way of doing this?
    Note that, as said, I don't use a disabled attribute, I do this by manipulating the form control itself, which seems to be The Good Way™.

  15. sandeep3333 commented on Nov 13, 2023

    @sandeep3333

    Hi @jnizet

    the bug occurs when using the formControlName within the formGroup.

    You can look in the 'profile-editor.component.html' component and compare two controls:

    1. <input [disabled]="true" id="street" type="text" formControlName="street" />

    Does not work.

    <input [disabled]="true" id="text_disable1" type="text"/>

    It works

    Here's an example:

    https://stackblitz.com/edit/angular-cu6mce?file=src/app/profile-editor/profile-editor.component.html

    I have the same issue so for the workaround i have used [readonly]

  16. polonmedia commented on Nov 29, 2023

    @polonmedia

    After update to angular 16 im trying to maintain [disabled] for radio button:
    image

    @dylhunn or anybody, can you help me with radio control value accessor?
    Im trying to implement this:
    image
    and I get following errors:
    error TS2322: Type 'ModuleWithProviders<FormsModule>' is not assignable to type 'readonly any[] | Type<any>'.
    and
    'imports' contains a ModuleWithProviders value, likely the result of a 'Module.forRoot()'-style call. These calls are not used to configure components and are not valid in standalone component imports - consider importing them in the application bootstrap instead.(-992012)

    how to get FormsModule.withConfig in standalone component? Angular ver 16.2.0

  17. jnizet commented on Nov 29, 2023

    @jnizet
    Contributor

    @polonmedia

    bootstrapApplication(AppComponent, { providers: [importProvidersFrom(FormsModule.withConfig(...), ...)
    
  18. johncrim commented on Jan 10, 2024

    @johncrim

    This is also a problem, in that a warning spews to the console if you use any of the formControl directives on a component, and the component has an @Input() disabled . IMO the benefit of the warning in a very specific case is not worth the noise in the general case.

    To state it another way: I don't have a problem with disabled not working with formControl directives. I have a problem that I'm getting an unnecessary warning (in dev builds) with erroneous guidance because I have an @Input() disabled on my components, if I use a formControl directive on those components. The name disabled is very useful and clear, and I think it's unreasonable to assume that it can only be used on the formControl directives.

    Eg:

    @Component({
      selector: 'ui-listbox',
      ...
      providers: [ {
        provide: NG_VALUE_ACCESSOR,
        useClass: SelectionValueAccessor,
        multi: true
      } ]
    })
    export class ListboxComponent<TKey> {
    
      @Input()
      set disabled(v: boolean) {
        this.focusArea.disabled = v;
        this.state.disabled = v;
      }
      get disabled() {
        return this.state.disabled;
      }
      
      ...
    }
    
    // Use in template:
    <ui-listbox [disabled]="disabled"
          [formControl]="formControl">

    gives me this warning even though nothing is wrong. Please remove the warning. I'm happy to provide a PR to remove it :)

  19. johncrim commented on Jan 10, 2024

    @johncrim

    I should add: formControl.disabled has different meaning from a typical component's MyComponent.disabled. When a FormControl is disabled, it means "exclude the values from this FormControl in the parent FormGroup values". For a web page element, or UI component, a disabled attribute typically means "appears inaccessible, cannot be focused, and value cannot be changed by user".

    I think it makes sense that FormControlDirective.disabled doesn't set FormControl.disabled. But it's problematic to have this erroneous warning, where the options are either to a) ignore a bunch of console warnings, or b) rename disabled to realDisabled or something equally irritating.

    See also: https://netbasal.com/disabling-form-controls-when-working-with-reactive-forms-in-angular-549dd7b42110

  20. EliseoPlunker commented on Mar 11, 2024

    @EliseoPlunker

    As temporal solution create a directive

    //see I use "disable" instead of "disabled"
    @Directive({
      selector: '[disable]',
      standalone:true
    })
    export class CustomDisabledDirective implements AfterViewInit {
      _disable:boolean
      @Input('disable') set _(value:boolean)
      {
        this._disable=value;
        this.ngAfterViewInit();
        
      }
      constructor(private elementRef:ElementRef) { }
      ngAfterViewInit()
      {
        if (this._disable)
        this.elementRef.nativeElement.setAttribute('disabled','true')
        else
        this.elementRef.nativeElement.removeAttribute('disabled')
      }
    
    
  21. pepijnmm commented on May 2, 2024

    @pepijnmm

    For use [disabled] already stopped working with angular 13 when it had a formControlName. We had to use [attr.disabled] from then on.

    It seems it was mentioned in the breaking changes but I agree that it could have been worded better release note

    We don't want the input to be disabled in the formGroup as validation still needs to be checked as the input gets filled in based on the other inputs from the user.
    So our solution is to change in our app.module.ts ReactiveFormModule to ReactiveFormsModule.withConfig({callSetDisabledState: 'whenDisabledForLegacyCode'})

    dylhunn explains it better in his comment: #48350 (comment)
    It would be nice if attr.disabled could be checked before it sets disabled on false again. but as far as I could tell that attribute was already removed and only disabled was still on the nativeElement.

  22. imasip commented on Jul 10, 2024

    @imasip

    left working from v.13 to v.17 but works when move control outside the form.

  23. Sanjeev-92 commented on Jul 18, 2024

    @Sanjeev-92

    i am using common component to render form after upgrade to angular 16 [attr.disabled] is not working without throwing any error.

    its a huge blunder in angular upgrade

  24. mahalinge commented on Aug 8, 2024

    @mahalinge

    am using [attr.disabled] in multiple comp template to disable based on conditions with formcontrol ,after upgrading to ang17 ,its stopped working ,any work around ?

  25. Laffery commented on Mar 13, 2025

    @Laffery

    Any progress?

  26. mrmokwa commented on Jun 12, 2025

    @mrmokwa

    Just want to give some feedback of my needs.

    I want to be able to disable my form control and display the form control errors at the same time.

  27. Stevetanus commented on Nov 13, 2025

    @Stevetanus

    The workaround from @EliseoPlunker

    export class CustomDisabledDirective implements AfterViewInit {
      _disable:boolean
      @Input('disable') set _(value:boolean)
      {
        this._disable=value;
        this.ngAfterViewInit();
        
      }
      constructor(private elementRef:ElementRef) { }
      ngAfterViewInit()
      {
        if (this._disable)
        this.elementRef.nativeElement.setAttribute('disabled','true')
        else
        this.elementRef.nativeElement.removeAttribute('disabled')
      }
    

    Then I did a little improvement as below using Renderer2

    import { AfterViewInit, Directive, ElementRef, Input, Renderer2 } from '@angular/core';
    
    @Directive({
      selector: '[disabledDirective]'
    })
    export class CustomDisabledDirective implements AfterViewInit{
      _disabled: boolean;
      @Input() set disabledDirective(value: boolean) {
        this._disabled = value;
        this.updateDisabledState();
      }
    
      constructor(private el: ElementRef, private renderer: Renderer2) { }
    
      ngAfterViewInit(): void {
        this.updateDisabledState();
      }
    
      updateDisabledState(): void {
        if (this._disabled) {
          this.renderer.setAttribute(this.el.nativeElement, 'disabled', '');
        } else {
          this.renderer.removeAttribute(this.el.nativeElement, 'disabled');
        }
      }
    }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Impacts a large percentage of users; if a workaround exists it is partial or overly painfularea: formsbugforms: directivesregressionIndicates than the issue relates to something that worked in a previous versionstate: confirmedworkaround2: non-obvious

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions