В этом сообщении блога используются реактивные формы, но примеры должны быть применимы и к формам на основе шаблонов.

Решение, предложенное в сообщении, в настоящее время, к сожалению, не на 100% охватывает настраиваемые элементы управления.

Как вы думаете, зачем писать в блоге об отключении формы? Как вы думаете, нет ничего проще, чем вызвать метод disable в элементе управления формой?

Это правда, отключить форму в Angular довольно просто. Это можно сделать, вызвав метод this.form.disable(). Это все, что нужно. Итак, давайте посмотрим на это в действии.

Обратите внимание на то, что элемент управления формой: электронная почта изначально отключен.

@Component(...)
export class AppComponent implements OnInit {
  form: FormGroup = this.fb.group({
    firstName: [null],
    lastName: [null],
    email: {value: null, disabled: true}
  });
  constructor(private fb: FormBuilder) {}
  ngOnInit() {
    // disable form after 2s
    setTimeout(() => this.form.disable(), 2000);
  }
}

Пока все в порядке, давайте снова включим форму после того, как мы ее отключили.

@Component(...)
export class AppComponent implements OnInit {
  form: FormGroup = this.fb.group({
    firstName: [null],
    lastName: [null],
    email: {value: null, disabled: true}
  });
  constructor(private fb: FormBuilder) {}
  ngOnInit() {
    // disable form after 2s
    setTimeout(() => this.form.disable(), 2000);
    // enable the form after 4s
    setTimeout(() => this.form.enable(), 4000);
  }
}

После повторного включения нашей формы есть разница с нашим начальным состоянием. Наш элемент управления формой электронной почты, который был отключен изначально, больше не отключен. Мы потеряли первоначальное отключенное состояние наших элементов управления формой. Возможно, это было намерением, но во многих случаях вы предпочитаете не терять это состояние.

Почему может быть полезно сохранить исходное состояние формы

Допустим, у нас есть вариант использования, когда почтовый индекс отключен по умолчанию, пока страна не выбрана из списка. Для заполнения списка стран мы используем внешний API.

В этом случае мы хотим отключить всю форму, пока не указаны страны. После их разрешения мы хотим снова включить форму, но не хотим терять исходное состояние элементов управления формой, поскольку почтовый индекс должен активироваться только при выборе страны. Единственное, что мы можем сделать, это повторно включить форму и отключить после нее элемент управления формой почтового индекса.

this.form.disable();
this.httpClient.get('https://api.example.com/countries')
  .subscribe((countries: Country[]) => {
    this.form.enable();
    this.form.controls.postalCode.disable();
  })

Должен быть лучший способ добиться этого? Давайте изучим!

Сброс формы

Как насчет сброса формы с помощью встроенной функции сброса в элементе управления формы?

this.form.reset();

Первоначальное включенное или отключенное состояние не принимается во внимание и поэтому остается неизменным при вызове метода.

Это означает, что мы теряем наше включенное / отключенное состояние, которое пытаемся сохранить. Давайте исследуем другой путь!

Отключенный атрибут

Когда мы либо включаем, либо отключаем нашу форму (группу), Angular фактически включает или отключает элементы управления внутри группы по отдельности. Причина этого в том, что в теге формы нет ничего похожего на отключенный атрибут, как на поле ввода.

Таким образом, отключение формы будет означать следующее.

<form>
  <input type=”text” name=”firstName” disabled />
  <input type=”text” name=”lastName” disabled />
</form>

Атрибут disabled не работает с тегом формы, поскольку его нет в спецификациях html.

<form disabled>
  <input type=”text” name=”firstName” />
  <input type=”text” name=”lastName” />
</form>

Хотя было бы неплохо. Итак, вернемся к исходной точке. Мы по-прежнему не можем сохранить наше включенное / отключенное состояние после отключения и / или включения формы.

Филдсет спешит на помощь

Доступен тег HTML, который является частью официальных спецификаций форм. Это называется набором полей.

Веб-документы MDN:

Элемент HTML ‹fieldset› используется для группировки нескольких элементов управления, а также ярлыков.

Если набор полей - это элемент, который группирует элементы управления вместе, возможно, он имеет свойства самого элемента управления формой, такие как отключено? И да, конечно.

Веб-документы MDN:

Если установлен логический атрибут disabled, все элементы управления формы, являющиеся потомками ‹fieldset›, будут отключены.

Давайте попробуем.

@Component({
  ...,
  template: `
    <form [formGroup]=”form” (ngSubmit)=”...”>
      <fieldset [disabled]=”isDisabled”>
        <div>
          <label for=”firstName”>First name</label
          <input type=”text” 
                 id=”firstName” 
                 formGroupName=”firstName” />
        </div>
        <div>
          <label for=”lastName”>Last name</label>
            <input type=”text” 
                   id=”lastName” 
                   formGroupName=”lastName” />
        </div>
        <div>
          <label for=”email”>Email</label>
          <input type=”email” 
                 id=”email” 
                 formGroupName=”email” />
        </div>
        <div>
          <button type=”submit”>Submit</button>
        </div>
      </fieldset>
    </form>
  `,
  ...
})
export class AppComponent implements OnInit {
  form: FormGroup = this.fb.group({
    firstName: [null],
    lastName: [null],
    email: {value: null, disabled: true}
  });
  isDisabled = false;
  constructor(private fb: FormBuilder) {}
  ngOnInit() {
  // disable form after 2s
  setTimeout(() => this.isDisabled = true, 2000);
  // enable the form after 4s
  setTimeout(() => this.isDisabled = false, 4000);
  }
}

Как видите, все работает нормально. Мы отключили всю форму без потери исходного состояния формы. Обратите внимание, что мы больше не используем методы .enable() и .disable().

Однако мы не хотим создавать набор полей снова и снова, поэтому давайте создадим на его основе директиву.

@Component({
  selector: '[disableForm]',
  styles: [`
    fieldset {
      display: block;
      margin: unset;
      padding: unset;
      border: unset;
    }
  `],
  template: `
    <fieldset [disabled]="disableForm">
      <ng-content></ng-content>
    </fieldset>
  `
})
export class DisableFormComponent {
  @Input('disableForm') disableForm: boolean;
  constructor() {}
  }
}

Теперь давайте воспользуемся директивой.

@Component({
  ...,
  template: `
    <form [formGroup]=”form” 
          [disableForm]="isDisabled" 
          (ngSubmit)=”...”>
     <!-- form controls ... -->    
    </form>
  `,
  ...
})
export class AppComponent implements OnInit {
  form: FormGroup = this.fb.group({
    firstName: [null],
    lastName: [null],
    email: {value: null, disabled: true}
  });
  isDisabled = false;
  constructor(private fb: FormBuilder) {}
  ngOnInit() {
    // disable form after 2s
    setTimeout(() => this.isDisabled = true, 2000);
    // enable the form after 4s
    setTimeout(() => this.isDisabled = false, 4000);
  }
}

Именно так мы создали способ отключить форму без потери ее включенного / отключенного состояния по умолчанию. И последнее, но не менее важное: поддержка браузера (https://caniuse.com/#feat=fieldset-disabled).

Заключение

Мы создали директиву, с помощью которой мы можем отключить форму, сохранив при этом включенное / отключенное состояние каждого элемента управления формой внутри. Довольно круто, нет? Имейте в виду, что этот метод предполагает, что вы не будете использовать функции включения и отключения, предоставляемые элементами управления формы.

Если вам понравился этот пост, обязательно поделитесь им со своими коллегами-программистами! Обратная связь приветствуется!

Особая благодарность этим людям за обзор:

Сэм Влёбергс, Килан Сават и Томас Вермёлен

Сайт: https://rubenvermeulen.be
Twitter: https://twitter.com/rubverm
Github: https://github.com/RubenVermeulen
LinkedIn : Https://www.linkedin.com/in/ruben-vermeulen/