Home > Enterprise >  Angular form mode toggling
Angular form mode toggling

Time:05-31

lets say that we have a very basic angular reactive form, with two modes: edit and show. And in show mode input fields are disabled, and in edit mode enabled:

<form [formGroup]="formGroup" (ngSubmit)="submitForm()" novalidate>
      <label for="fname">First name:</label><br />
      <input
        type="text"
        id="fname"
        name="fname"
        formControlName="fname"
      /><br />
      <label for="lname">Last name:</label><br />
      <input
        type="text"
        id="lname"
        name="lname"
        formControlName="lname"
      /><br /><br />
      <button type="submit">{{formMode == 'show' ? 'Edit' : 'Submit'}}</button>
    </form>
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  formGroup: FormGroup;
  formMode: 'show' | 'edit' = 'show';

  constructor(private fb: FormBuilder) {}

  ngOnInit(): void {
    this.formGroup = this.fb.group({
      fname: [{ value: 'John', disabled: this.formMode == 'show' }],
      lname: [{ value: 'Doe', disabled: this.formMode == 'show' }],
    });
  }

  submitForm() {
    if (this.formMode === 'edit') {
      console.log(this.formGroup.value);
      this.formMode = "show"
    } else {
      this.formMode = "edit"
    }
  }

However when the state od formMode gets toggled from show to edit state, disabled state of the formControls doesn't change.

I know I can use this.formGroup.enable() and this.formGroup.disable(), but the that what be a mess if there was a large form and only some fields are toggling state.

I have expected that this disabled prop to be reactive since this.formMode is reactive by default...

Here is a blitz: https://stackblitz.com/edit/angular-ivy-4zybd7?file=src/app/app.component.ts

CodePudding user response:

We can create a custom directive and apply it on the form element, then Inject formGroup directive inside directive to get all formControl paths then based on our conditions you can disable or enable.

What is the use of formControlPath?

FormGroup can have multiple formControl.To get all formControl name as array using this.control.directives.map((dir) => dir.path.join())

Why using Promise.resolve?

If you console.log(this.formControlPath) inside ngOnChanges It would be empty. To get all registered formControl key inside ngOnChanges use setTimeOut or Promise.resolve.

custom.directive.ts

import { Directive, Input, OnChanges, OnInit } from '@angular/core';
import { FormGroupDirective, NgControl } from '@angular/forms';

export type FormMode = 'show' | 'edit';

@Directive({
  selector: '[appCanDisable]',
})
export class CanDisableDirective implements OnChanges {
  @Input() formMode: FormMode;
  @Input() disableFields: string[]; //Field you want to disable pass it from parent compinent
  get formControlPath(): string[] {
    return this.control.directives.map((dir) => dir.path.join());
  }
  constructor(private control: FormGroupDirective) {}
  ngOnChanges() {
    Promise.resolve().then(() => {
      const state = this.formMode === 'show' ? 'disable' : 'enable';
      this.formControlPath.forEach((path) => {
        if (this.disableFields.find((field) => field === path)) {
          this.control.form.get(path)[state]();
        }
      });
    });
  }
}

Working Example

  • Related