Bardziej zaawansowane elementy sterujące formularzem

Dzięki nowemu zdarzeniu i interfejsom API elementów niestandardowych wypełnianie formularzy stało się znacznie łatwiejsze.

Arthur Evans

Wielu deweloperów tworzy niestandardowe elementy formularza, aby udostępnić elementy, których nie ma w przeglądarce, lub dostosować wygląd i wygląd w sposób wykraczający poza możliwości wbudowanych elementów formularza.

Może być jednak trudno odtworzyć funkcje wbudowanych elementów sterujących formularza HTML. Oto niektóre funkcje, które element <input> otrzymuje automatycznie po dodaniu go do formularza:

  • Dane są automatycznie dodawane do listy elementów sterujących formularza.
  • Wartość pola jest automatycznie przesyłana wraz z formularzem.
  • Dane wejściowe są uwzględniane w weryfikacji formularza. Stylowanie pola wejściowego możesz określić za pomocą pseudoklas :valid:invalid.
  • Dane są przekazywane, gdy formularz jest resetowany, gdy jest ponownie wczytywany lub gdy przeglądarka próbuje automatycznie wypełnić pola formularza.

Elementy sterujące formularza niestandardowego zwykle mają niewiele z tych funkcji. Deweloperzy mogą obejść niektóre ograniczenia występujące w kodzie JavaScriptu, takie jak dodanie do formularza ukrytego <input>, aby umożliwić przesyłanie formularzy. Innych funkcji nie można jednak odtworzyć tylko za pomocą JavaScriptu.

2 nowe funkcje internetowe ułatwiają tworzenie niestandardowych elementów sterujących formularzem i eliminują ograniczenia obecnych elementów sterujących:

  • Zdarzenie formdata umożliwia dowolnemu obiektowi JavaScriptu udział w przesyłaniu formularza, dzięki czemu możesz dodawać dane formularza bez użycia ukrytego obiektu <input>.
  • Interfejs API elementów niestandardowych związanych z formularzem pozwala tym elementom działać bardziej jak wbudowane elementy sterujące formularza.

Dzięki tym 2 funkcjom można tworzyć nowe, lepiej działające ustawienia.

Interfejs API oparty na zdarzeniach

Zdarzenie formdata to interfejs API niskiego poziomu, który umożliwia dowolnemu kodom JavaScript udział w przesyłaniu formularzy. Mechanizm działa w ten sposób:

  1. Dodaj detektor zdarzeń formdata do formularza, z którym chcesz korzystać.
  2. Gdy użytkownik kliknie przycisk przesyłania, formularz uruchamia zdarzenie formdata, które zawiera obiekt FormData zawierający wszystkie przesyłane dane.
  3. Każdy detektor formdata ma możliwość dodania lub zmiany danych przed przesłaniem formularza.

Oto przykład wysyłania pojedynczej wartości w obiekcie formdata:

const form = document.querySelector('form');
// FormData event is sent on <form> submission, before transmission.
// The event has a formData property
form.addEventListener('formdata', ({formData}) => {
  // https://developer.mozilla.org/docs/Web/API/FormData
  formData.append('my-input', myInputValue);
});

Wypróbuj to, korzystając z naszego przykładu na Glitch. Aby sprawdzić działanie interfejsu API, uruchom go w Chrome 77 lub nowszej wersji.

Zgodność z przeglądarką

Obsługa przeglądarek

  • Chrome: 5.
  • Edge: 12.
  • Firefox: 4.
  • Safari: 5.

Źródło

Elementy niestandardowe powiązane z formularzem

Interfejsu API opartego na zdarzeniach możesz używać z dowolnym komponentem, ale umożliwia on tylko interakcję z procesem przesyłania.

Ustandaryzowane elementy sterujące formularza są uwzględniane w wielu częściach cyklu życia formularza poza jego przesyłaniem. Elementy niestandardowe powiązane z formularzami mają za zadanie wypełnić lukę między niestandardowymi widżetami a wbudowanymi elementami sterującymi. Elementy niestandardowe powiązane z formularzem mają wiele funkcji standardowych elementów formularzy:

  • Gdy umieścisz element niestandardowy powiązany z formularzem w elementach <form>, zostanie on automatycznie powiązany z formularzem, tak jak element kontrolny udostępniony przez przeglądarkę.
  • Element może być oznaczony za pomocą elementu <label>.
  • Element może ustawiać wartość, która jest automatycznie przesyłana z formularzem.
  • Element może ustawić flagę wskazującą, czy ma prawidłowy element wejściowy. Jeśli jedno z elementów sterujących formularza zawiera nieprawidłowe dane wejściowe, formularz nie zostanie przesłany.
  • Element może zapewniać wywołania zwrotne w różnych częściach cyklu życia formularza, np. gdy formularz jest wyłączony lub zresetowany do stanu domyślnego.
  • Element obsługuje standardowe pseudoklasy CSS dla elementów sterujących formularzem, takich jak :disabled:invalid.

To mnóstwo funkcji. W tym artykule nie omawiamy wszystkich tych funkcji, ale opisujemy podstawy potrzebne do zintegrowania elementu niestandardowego z formularzem.

Definiowanie elementu niestandardowego powiązanego z formularzem

Aby przekształcić element niestandardowy w niestandardowy element powiązany z formularzem, trzeba wykonać kilka dodatkowych czynności:

  • Dodaj stałą właściwość formAssociated do klasy elementu niestandardowego. Informuje przeglądarkę, że element ma być traktowany jak element sterujący formularzem.
  • Aby uzyskać dostęp do dodatkowych metod i właściwości elementów sterujących formularzem, takich jak setFormValue()setValidity(), wywołaj metodę attachInternals() elementu.
  • Dodaj typowe właściwości i metody obsługiwane przez elementy formularzy, takie jak name, valuevalidity.

Oto jak te elementy pasują do podstawowej definicji elementu niestandardowego:

// Form-associated custom elements must be autonomous custom elements--
// meaning they must extend HTMLElement, not one of its subclasses.
class MyCounter extends HTMLElement {

  // Identify the element as a form-associated custom element
  static formAssociated = true;

  constructor() {
    super();
    // Get access to the internal form control APIs
    this.internals_ = this.attachInternals();
    // internal value for this control
    this.value_ = 0;
  }

  // Form controls usually expose a "value" property
  get value() { return this.value_; }
  set value(v) { this.value_ = v; }

  // The following properties and methods aren't strictly required,
  // but browser-level form controls provide them. Providing them helps
  // ensure consistency with browser-provided controls.
  get form() { return this.internals_.form; }
  get name() { return this.getAttribute('name'); }
  get type() { return this.localName; }
  get validity() {return this.internals_.validity; }
  get validationMessage() {return this.internals_.validationMessage; }
  get willValidate() {return this.internals_.willValidate; }

  checkValidity() { return this.internals_.checkValidity(); }
  reportValidity() {return this.internals_.reportValidity(); }

  
}
customElements.define('my-counter', MyCounter);

Po zarejestrowaniu możesz używać tego elementu wszędzie tam, gdzie używasz kontrolek formularza udostępnianych przez przeglądarkę:

<form>
  <label>Number of bunnies: <my-counter></my-counter></label>
  <button type="submit">Submit</button>
</form>

Ustawianie wartości

Metoda attachInternals() zwraca obiekt ElementInternals, który zapewnia dostęp do interfejsów API elementów sterujących formularzem. Najprostszą z nich jest metoda setFormValue(), która ustawia bieżącą wartość elementu sterującego.

Metoda setFormValue() może przyjmować 1 z 3 typów wartości:

  • wartość typu ciąg znaków;
  • Obiekt File.
  • Obiekt FormData. Obiekt FormData może przekazywać wiele wartości (np. kontrolka wprowadzania danych karty kredytowej może przekazywać numer karty, datę ważności i kod weryfikacyjny).

Aby ustawić prostą wartość:

this.internals_.setFormValue(this.value_);

Aby ustawić wiele wartości, wykonaj następujące czynności:

// Use the control's name as the base name for submitted data
const n = this.getAttribute('name');
const entries = new FormData();
entries.append(n + '-first-name', this.firstName_);
entries.append(n + '-last-name', this.lastName_);
this.internals_.setFormValue(entries);

weryfikacja danych wejściowych;

Element może też uczestniczyć w weryfikacji formularza przez wywołanie metody setValidity() obiektu internals.

// Assume this is called whenever the internal value is updated
onUpdateValue() {
  if (!this.matches(':disabled') && this.hasAttribute('required') &&
      this.value_ < 0) {
    this.internals_.setValidity({customError: true}, 'Value cannot be negative.');
  }
  else {
    this.internals_.setValidity({});
  }
  this.internals.setFormValue(this.value_);
}

Element niestandardowy powiązany z formularzem możesz stylizować za pomocą pseudoklas :valid:invalid, tak jak wbudowany element formularza.

Wywołania zwrotne cyklu życia

Interfejs API elementu niestandardowego powiązanego z formularzem zawiera zestaw dodatkowych wywołań zwrotnych cyklu życia, które są powiązane z cyklem życia formularza. Zwróć uwagę, że funkcje zwrotne są opcjonalne: implementuj je tylko wtedy, gdy element musi coś wykonać w danym momencie cyklu życia.

void formAssociatedCallback(form)

Jest wywoływany, gdy przeglądarka powiąże element z elementem formularza lub odłączy go od elementu formularza.

void formDisabledCallback(disabled)

Wywoływana po zmianie stanu disabled elementu, ponieważ atrybut disabled tego elementu został dodany lub usunięty albo stan disabled zmienił się w elemencie <fieldset>, który jest elementem nadrzędnym tego elementu. Parametr disabled reprezentuje nowy stan wyłączenia elementu. Element może na przykład wyłączać elementy w modelu Shadow DOM, gdy jest wyłączony.

void formResetCallback()

Jest wywoływany po zresetowaniu formularza. Element powinien zresetować się do stanu domyślnego. W przypadku elementów <input> zazwyczaj wymaga to skonfigurowania właściwości value tak, aby pasowała do atrybutu value ustawionego w znacznikach (lub w przypadku pola wyboru – dostosowanie właściwości checked do atrybutu checked.

void formStateRestoreCallback(state, mode)

Połączenie zostało nawiązane w jednym z tych 2 sposobów:

  • Gdy przeglądarka przywróci stan elementu (np. po przejściu nawigacji lub ponownym uruchomieniu przeglądarki). W tym przypadku argument mode ma wartość "restore".
  • Gdy funkcje wspomagania wprowadzania danych w przeglądarce, np. autouzupełnianie formularzy, ustawiają wartość. W tym przypadku argument mode ma wartość "autocomplete".

Typ pierwszego argumentu zależy od sposobu wywołania metody setFormValue(). Więcej informacji znajdziesz w artykule Przywracanie stanu formularza.

Przywracanie stanu formularza

W niektórych okolicznościach, np. gdy użytkownik wróci na stronę lub uruchomi ponownie przeglądarkę, może ona spróbować przywrócić formularz do stanu, w którym był on przed zamknięciem.

W przypadku elementu niestandardowego powiązanego z formularzem przywrócone stan pochodzi z wartości przekazanych metodzie setFormValue(). Metodę możesz wywołać z jednym parametrem wartości, jak w wcześniejszych przykładach, lub z 2 parametrami:

this.internals_.setFormValue(value, state);

value reprezentuje wartość elementu sterującego, którą można przesłać. Opcjonalny parametr state to wewnętrzna reprezentacja stanu elementu sterującego, która może zawierać dane, które nie są wysyłane na serwer. Parametr state może przyjmować te same typy co parametr value – może to być ciąg znaków, obiekt File lub FormData.

Parametr state jest przydatny, gdy nie możesz przywrócić stanu elementu sterującego na podstawie samej wartości. Załóżmy na przykład, że tworzysz selektor kolorów z kilkoma trybami: paletą lub kołem kolorów RGB. Wartość, którą można przesłać, to kolor wybrany w postaci kanonicznej, np. "#7fff00". Aby jednak przywrócić kontroler do określonego stanu, musisz też wiedzieć, w jakim trybie był wcześniej. Stan może wyglądać tak: "palette/#7fff00".

this.internals_.setFormValue(this.value_,
    this.mode_ + '/' + this.value_);

Twój kod musi przywrócić stan na podstawie zapisanej wartości stanu.

formStateRestoreCallback(state, mode) {
  if (mode == 'restore') {
    // expects a state parameter in the form 'controlMode/value'
    [controlMode, value] = state.split('/');
    this.mode_ = controlMode;
    this.value_ = value;
  }
  // Chrome currently doesn't handle autofill for form-associated
  // custom elements. In the autofill case, you might need to handle
  // a raw value.
}

W przypadku prostszego elementu sterującego (np. pola tekstowego z liczbą) wartość prawdopodobnie wystarczy do przywrócenia poprzedniego stanu. Jeśli pominiesz parametr state, gdy wywołujesz funkcję setFormValue(), jego wartość zostanie przekazana do parametru formStateRestoreCallback().

formStateRestoreCallback(state, mode) {
  // Simple case, restore the saved value
  this.value_ = state;
}

Przykład działającego kodu

W tym przykładzie wykorzystano wiele funkcji elementów niestandardowych powiązanych z formularzem. Aby sprawdzić działanie interfejsu API, uruchom go w Chrome 77 lub nowszej wersji.

Wykrywanie cech

Wykrywanie funkcji pozwala określić, czy zdarzenie formdata i elementy niestandardowe powiązane z formularzem są dostępne. Obecnie nie ma żadnych polyfilli dla żadnej z tych funkcji. W obu przypadkach możesz wrócić do dodania ukrytego elementu formularza, aby przekazać wartość ustawienia do formularza. Wiele zaawansowanych funkcji elementów niestandardowych powiązanych z formularzami prawdopodobnie będzie trudno lub w ogóle niemożliwie wypełnić za pomocą funkcji polyfill.

if ('FormDataEvent' in window) {
  // formdata event is supported
}

if ('ElementInternals' in window &&
    'setFormValue' in window.ElementInternals.prototype) {
  // Form-associated custom elements are supported
}

Podsumowanie

Elementy niestandardowe powiązane z zdarzeniem formdata i formularzem udostępniają nowe narzędzia do tworzenia niestandardowych elementów sterujących formularza.

Zdarzenie formdata nie zapewnia żadnych nowych możliwości, ale zapewnia interfejs do dodawania danych formularza do procesu przesyłania bez konieczności tworzenia ukrytego elementu <input>.

Interfejs API elementów niestandardowych powiązanych z formularzem udostępnia nowy zestaw funkcji do tworzenia niestandardowych elementów formularza, które działają jak wbudowane elementy formularza.

Baner powitalny autorstwa Oudom Pravat z Unsplash.