Phần tử tuỳ chỉnh liên kết với biểu mẫu

Đừng quay lại <input> ẩn để hiển thị giá trị của thành phần điều khiển tuỳ chỉnh cho biểu mẫu. Sử dụng Phần tử tuỳ chỉnh liên kết với biểu mẫu (FACE): với formAssociated và ElementInternals, phần tử này sẽ gửi giá trị riêng, tham gia vào quá trình xác thực và phản ánh trạng thái biểu mẫu theo cách tự nhiên.

class RatingInput extends HTMLElement {
  // MANDATORY: opt in. Without this, attachInternals() form APIs are unavailable.
  static formAssociated = true;

  #internals;
  #value = '';

  constructor() {
    super();
    this.#internals = this.attachInternals();
    this.attachShadow({ mode: 'open', delegatesFocus: true });
  }

  connectedCallback() {
    // Validate initial state once attributes (such as `required`) are present.
    this.#validate();
  }

  get value() { return this.#value; }
  set value(v) {
    this.#value = v;
    // Push the value into the owning <form> for submission.
    this.#internals.setFormValue(v);
    this.#validate();
  }

  #validate() {
    if (this.hasAttribute('required') && !this.#value) {
      // Mark invalid and supply the native validation message + anchor.
      this.#internals.setValidity(
        { valueMissing: true },
        'Please choose a rating.',
        this.shadowRoot.querySelector('[tabindex]'),
      );
    } else {
      this.#internals.setValidity({}); // clear
    }
  }

  // Optional FACE lifecycle hooks:
  formResetCallback() { this.value = ''; }
  formDisabledCallback(disabled) {
    // Disable internal controls so they leave the tab order and ignore clicks.
    // Do NOT toggle `disabled` on `this`, or a `<fieldset disabled>` toggle will
    // leave the host permanently stuck with its own `disabled` attribute.
    for (const el of this.shadowRoot.querySelectorAll('button')) {
      el.disabled = disabled;
    }
  }
  formStateRestoreCallback(state) { this.value = state; }
}
customElements.define('rating-input', RatingInput);

Điểm chính:

  • static formAssociated = true là BẮT BUỘC; thuộc tính này mở khoá các phương thức ElementInternals liên quan đến biểu mẫu và vòng đời formXxxCallback.
  • setFormValue(value) là nút gửi, không cần có dữ liệu đầu vào ẩn. Truyền FormData cho các chế độ kiểm soát nhiều giá trị.
  • setValidity(flags, message, anchor) tích hợp với quy trình xác thực quy tắc ràng buộc gốc: biểu mẫu sẽ không gửi khi không hợp lệ, :invalid/:valid áp dụng và phần tử neo nhận được tiêu điểm trên báo cáo. Gọi setValidity({}) để xoá. Các lớp giả :user-valid/:user-invalid nhận biết hoạt động tương tác không được kết nối nhất quán cho các phần tử liên kết với biểu mẫu trên các trình duyệt — hãy xác minh khả năng hỗ trợ trước khi dựa vào các lớp giả này thay vì giả định rằng các lớp giả này theo dõi hoạt động tương tác của người dùng theo cách chúng thực hiện cho các thành phần điều khiển gốc.
  • Triển khai formResetCallback, formDisabledCallback và formStateRestoreCallback để phần tử hoạt động giống như một thành phần điều khiển gốc khi đặt lại, fieldset[disabled] và khôi phục tính năng tự động điền lùi/tiến. Trong formDisabledCallback(disabled), hãy tắt các chế độ điều khiển nội bộ (hoặc xoá các chế độ này khỏi thứ tự thẻ và bỏ qua dữ liệu đầu vào) thay vì đặt disabled trên máy chủ lưu trữ — việc đặt thuộc tính máy chủ lưu trữ bên trong lệnh gọi lại sẽ ngăn việc bật lại khi <fieldset> của đối tượng cấp trên được bật lại và máy chủ lưu trữ đã tự động khớp với :disabled.
  • Tuân theo quy ước của nền tảng đối với các sự kiện được gửi đi và ưu tiên các sự kiện input và change tích hợp sẵn hơn các sự kiện tuỳ chỉnh. input kích hoạt khi giá trị thay đổi do hành động trực tiếp của người dùng (nhập, kéo thanh trượt); change kích hoạt một lần cho mỗi luồng thay đổi, khi người dùng xác nhận (thả thanh trượt, làm mờ trường). Tạo các đối tượng này bằng InputEvent và Event tương ứng.

Chiến lược dự phòng

Phát hiện tính năng bằng tính năng hỗ trợ static formAssociated trước khi dựa vào tính năng đó: 'attachInternals' in HTMLElement.prototype && 'setFormValue' in ElementInternals.prototype. Nếu không có, hãy quay lại <input> gốc bị ẩn trực quan được đồng bộ hoá với giá trị của thành phần điều khiển để biểu mẫu vẫn gửi – trường hợp duy nhất mà mẫu đầu vào ẩn được chứng minh là hợp lý.