angularangular-reactive-formsangular-formsangular-signals

How to disable/enable reactive form using signals


I want to disable/enable a formGroup, based on a signal input. I do not want to use [disabled] to toggle the form.

@Component({
  selector: 'app-child',
  standalone: true,
  imports: [ReactiveFormsModule],
  template: `
    <form [formGroup]="form">
      <input formControlName="test"/>
    </form>
  `,
})
export class Child {
  disableForm = input(false);
  name = 'Angular';
  form = new FormGroup({
    test: new FormControl(1),
  });

  ngOnInit() {}
}

Parent passes in the input like below.

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [Child],
  template: `
    <app-child [disableForm]="disableForm()"/>
    <button (click)="toggleDisabled()">toggle disable</button>
  `,
})
export class App {
  disableForm = signal(false);

  toggleDisabled() {
    this.disableForm.update((value: boolean) => !value);
  }
}

Looking for a method that triggers/reacts based on the change of the signal.


Solution

  • This can be done with input signal, basically we use effect to perform the logic when the signal changes, based on that we enable and disable the form using disableForm or enableForm.

    disableForm = input(false);
    ...
    
    ...
    constructor() {
      effect(() => {
        if (this.disableForm()) {
          this.form.disable();
        } else {
          this.form.enable();
        }
      });
    }
    

    Then on button press, we toggle the signal using update method, inside the update, we get the previous value and the returned value will be updated as the new value.

    disableForm = signal(false);
    
    toggleDisabled() {
      this.disableForm.update((value: boolean) => !value);
    }
    

    The signal, can be passed as input to the child component.

    <app-child [disableForm]="disableForm()"/>
    <button (click)="toggleDisabled()">toggle disable</button>
    

    Full Code:

    import { Component, effect, input, signal } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
    
    @Component({
      selector: 'app-child',
      standalone: true,
      imports: [ReactiveFormsModule],
      template: `
        <form [formGroup]="form">
          <input formControlName="test"/>
        </form>
      `,
    })
    export class Child {
      disableForm = input(false);
      name = 'Angular';
      form = new FormGroup({
        test: new FormControl(1),
      });
    
      constructor() {
        effect(() => {
          if (this.disableForm()) {
            this.form.disable();
          } else {
            this.form.enable();
          }
        });
      }
    
      ngOnInit() {}
    }
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [Child],
      template: `
        <app-child [disableForm]="disableForm()"/>
        <button (click)="toggleDisabled()">toggle disable</button>
      `,
    })
    export class App {
      disableForm = signal(false);
    
      toggleDisabled() {
        this.disableForm.update((value: boolean) => !value);
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo