71

I have a reactive form. On edit I want a control disabled.

The below works:

   this.myForm.controls['analysis_horizon'].disable();

However, the key analysis_horizon is no longer in my myForm.value hash.

How do I disable a field with a reactive form but keeping the value in the form values hash?

I tried [disabled]= but I get the below:

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: 
      form = new FormGroup({
        first: new FormControl({value: 'Nancy', disabled: true}, Validators.required),
        last: new FormControl('Drew', Validators.required)
      });

I load data from my database on edit into the form controls but I need one field to not be allowed to change.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Tampa
  • 75,446
  • 119
  • 278
  • 425
  • it tells you to use disabled attribute directly in HTML, rather than using it with formcontrol. – micronyks Feb 11 '17 at 20:14
  • Given that it's not changing, you could put it back into the value yourself for whatever is consuming it, with `Object.assign` or something. – jonrsharpe Feb 11 '17 at 20:20
  • 1
    Maybe I'm misunderstanding but, why not do like the message suggests: `yourFormControl: new FormControl({value: yourPresetValue, disabled: true})` – AT82 Feb 11 '17 at 20:22
  • Possible duplicate of [Angular 2 disabled controls do not get included in the form.value](https://stackoverflow.com/questions/40148102/angular-2-disabled-controls-do-not-get-included-in-the-form-value) – rmcsharry Nov 14 '18 at 08:46
  • 1
    I know this is old now, but reactive forms has another method that's called form.getRawValue() that would do this for you I cant say it works 100% of the time but it works when you use any of the setValue or patch Value methods – Pari Baker May 24 '19 at 01:14
  • Angular 11 getRawValue() doesn't include disabled controls even if using patchvalue. – thinkOfaNumber Mar 29 '21 at 06:34

8 Answers8

156

You could use getRawValue() instead of the value property. According to the documentation:

The aggregate value of the FormGroup, including any disabled controls.

If you'd like to include all values regardless of disabled status, use this method. Otherwise, the value property is the best way to get the value of the group.

this.myForm.getRawValue()
I. Ajeneza
  • 1,618
  • 1
  • 8
  • 7
12

My solution is using [attr.disabled]:

<select formControlName="foo"
  [attr.disabled]="disabled == true ? true : null"> 
</select>

Assuming you have a disabled property in your component. You have to retun null not false to enable the input.

pogiaron
  • 332
  • 3
  • 5
9

Here is another option if you don't want to use getRawValue which changes the normal way of getting the value from forms. More reasons why I like readonly better: https://stackoverflow.com/a/7730719/1678151.

My solution also shows how to fill a form control.

component.ts

fillForm(data: any){
 if (data != null && data.firstName){
   this.form.patchValue({
     firstName: data.firstName
   })    
   this.firstNameIsReadOnly = true;
 } 

template.html

<input formControlName="firstName" [readonly]='firstNameIsReadOnly'>

styles.scss

input[readonly] {
 color: rgba(0,0,0,.38);
}
Anthony
  • 2,330
  • 8
  • 44
  • 64
6

Try add onlySelf: true inside method disable:

this.myForm.controls['analysis_horizon'].disable({onlySelf: true});
garfbradaz
  • 3,424
  • 7
  • 43
  • 70
Vasyl Karpa
  • 85
  • 1
  • 5
4

I came across this problem whilst trying to implement the Angular Material Datepicker with a reactive form - I wanted the input to be disabled so only the picker can set the date, however Angular shouts at you when you use the disabled attribute in the template, and then removes the value from the form when you do it in the component form group code.

The solution I came up with is to use CSS to prevent interaction with the input:

.disable-pointer-events {
   pointer-events: none;
}
<input
  class="disable-pointer-events"
  formControlName="yourControlName"
/>
2

To handle that elegantly without losing the value of the formControl is to use

[readonly]= condition ? true : false
Shoaib Khalil
  • 1,874
  • 17
  • 10
0

Looking for the solution to this problem I found another elegant aprouch changing the attribute again in the onSubmit function.

onSubmit() {
console.log('Before', this.myForm.value);

 this.myForm.controls['analysis_horizon'].enable();  // if edit

console.log('After', this.myForm.value);

}

Regards

nicpas
  • 91
  • 1
  • 6
0

I usually use the following approach (don't pay attention to custom controls - Material controls will also go, fb is a FormBuilder DI):

Code I use:

          this.addressTypeForm = this.fb.group({
            id: [null],
            ...
          });
          this.addressTypeForm.patchValue(this.selectedAddressType); //or patch only specific fields

selectedAddressType contains the data read from some external source and is supposed to be "read-only". Then specific data is copied into form via patchValue. If I don't want to let something be changed - e.g. id - I just output the read-only source data instead of the form.

Markup I use:

          <ng-container
            *ngIf="isNewAddressType; then addAddressTypeSelection; else editAddressTypeLabel"
          ></ng-container>
          <ng-template #addAddressTypeSelection>
            <bc-text
              [label]="'::AddressManagement:Id' | abpLocalization"
              [error]="abxForm.get('id').invalid && abxForm.get('id').touched"
              formControlName="id"
            ></bc-text>
          </ng-template>
          <ng-template #editAddressTypeLabel>
            <bc-text
              *ngIf="selectedAddressType"
              [label]="'::AddressManagement:Id' | abpLocalization"
              [disabled]="true"
              [value]="selectedAddressType.id"
            ></bc-text>
          </ng-template>

If you eventually are going to save the data from your form - which is logical - you don't want to disable its specific controls, because in this case you will loose their values. For instance, if you have id control disabled from the start (or disable it in other way dynamically):

          this.addressTypeForm = this.fb.group({
            id: [{ value: 111, disabled: true }],
            ...
          })

it will be displayed disabled here automatically:

            <bc-text
              [label]="'::AddressManagement:Id' | abpLocalization"
              [error]="abxForm.get('id').invalid && abxForm.get('id').touched"
              formControlName="id"
            ></bc-text>

But while saving such form, you will loose id value.

I usually disable and enable a whole form. But never - a specific form control (unless I intentionally want to "loose" its value, but I've never had such cases).

Alexander
  • 1,152
  • 1
  • 16
  • 18