Интеграция с HTML-формами

FAST Element — это современная библиотека для создания веб-компонентов на основе стандартов Web Components. Одной из ключевых задач при разработке интерактивных интерфейсов является интеграция кастомных элементов с HTML-формами, чтобы они могли работать так же, как стандартные элементы <input>, <select> и <textarea>.


Подключение FAST Element к форме

Для того чтобы компонент мог участвовать в отправке формы, он должен реализовать интерфейс формы, то есть:

  1. Поддерживать атрибут name.
  2. Хранить текущее значение в свойствах.
  3. Корректно реагировать на события изменения (change, input).
  4. Уметь сбрасываться вместе с формой.

FAST Element предоставляет свойство value, которое является ключевым для интеграции с формой.

import { FASTElement, html, css, customElement, observable } from "@microsoft/fast-element";

const template = html<MyInput>`
  <input
    type="text"
    .value="${x => x.value}"
    @input="${x => x.handleInput()}"
  />
`;

const styles = css`
  input {
    font-size: 16px;
    padding: 4px;
  }
`;

@customElement({ name: 'my-input', template, styles })
export class MyInput extends FASTElement {
  @observable value = '';

  handleInput() {
    const input = this.shadowRoot.querySelector('input');
    this.value = input.value;
    this.$emit('change');
  }
}

В этом примере компонент синхронизирует своё свойство value с внутренним <input>, а событие change позволяет форме отслеживать изменения значения.


Реализация интерфейса формы

Для полноценной работы с формами компонент должен реализовывать методы:

  • formAssociatedCallback() — вызывается, когда элемент привязан к форме.
  • formDisabledCallback(disabled) — применяется при отключении формы.
  • formResetCallback() — сброс значения при вызове метода reset() формы.
  • formStateRestoreCallback(state, mode) — восстановление состояния при навигации или перезагрузке страницы.

FAST Element позволяет использовать Native Form Support через HTMLElementInternals:

@customElement({ name: 'my-input', template, styles, formAssociated: true })
export class MyInput extends FASTElement {
  static formAssociated = true;
  internals = this.attachInternals();

  @observable value = '';

  handleInput() {
    this.value = this.shadowRoot.querySelector('input').value;
    this.internals.setFormValue(this.value);
  }

  formResetCallback() {
    this.value = '';
    this.internals.setFormValue(this.value);
  }
}

attachInternals() создаёт объект ElementInternals, который управляет значением элемента для формы. Метод setFormValue() синхронизирует значение с родительской формой.


События и реактивность

FAST Element использует декоратор @observable, который автоматически отслеживает изменения свойства и обновляет DOM. Для работы с формами важно также корректно отправлять события:

  • input — при каждом изменении значения.
  • change — при завершении редактирования (например, при потере фокуса).
handleInput() {
  const input = this.shadowRoot.querySelector('input');
  this.value = input.value;
  this.internals.setFormValue(this.value);
  this.$emit('input');
}

Использование @observable гарантирует синхронизацию с шаблоном:

<my-input value="Привет"></my-input>

Любые изменения свойства value автоматически обновят внутренний <input>.


Валидация и ограничения

Для полноценной интеграции с формами необходимо поддерживать стандартные атрибуты валидации: required, minlength, maxlength, pattern.

formCheckValidity() {
  const input = this.shadowRoot.querySelector('input');
  return input.checkValidity();
}

reportValidity() {
  const input = this.shadowRoot.querySelector('input');
  return input.reportValidity();
}

Эти методы позволяют компоненту участвовать в стандартной валидации формы. ElementInternals также поддерживает setValidity() для кастомной проверки:

this.internals.setValidity({ valueMissing: true }, "Поле обязательно для заполнения", input);

Связывание с родительской формой

При добавлении элемента в <form> его значение автоматически включается в объект формы при вызове FormData или submit. Важно, чтобы свойство name было корректно установлено:

<form id="myForm">
  <my-input name="username"></my-input>
  <button type="submit">Отправить</button>
</form>
const form = document.getElementById('myForm');
form.addEventListener('submit', e => {
  e.preventDefault();
  const formData = new FormData(form);
  console.log(formData.get('username'));
});

FormData будет содержать текущее значение my-input, благодаря использованию internals.setFormValue().


Поддержка сброса формы

Элементы FAST Element с интеграцией форм автоматически обрабатывают событие reset формы. Для этого достаточно реализовать formResetCallback:

formResetCallback() {
  this.value = '';
  this.internals.setFormValue(this.value);
}

После вызова form.reset() компонент вернётся к исходному состоянию, аналогично стандартным элементам <input>.


Использование кастомных событий для сложных форм

В сложных сценариях можно объединять несколько элементов FAST Element в единую форму, передавая значения через кастомные события и свойства. Важно:

  • Отправлять события input и change.
  • Использовать @observable для автоматического обновления шаблона.
  • Поддерживать internals.setFormValue() для правильной работы FormData.
this.$emit('change', { value: this.value });

Совместимость с библиотеками и фреймворками

Благодаря стандартам Web Components, FAST Element совместим с React, Vue, Angular и чистым HTML. Интеграция с формами остаётся стандартной, если компонент реализует:

  • name
  • value
  • Методы формы через ElementInternals

Это позволяет использовать FAST Element как полноценную замену стандартных элементов формы, сохраняя реактивность и валидацию.


Интеграция FAST Element с HTML-формами превращает кастомные элементы в полноценные, нативные участники процесса отправки и валидации данных, сохраняя реактивность и гибкость, которые предоставляет библиотека.