องค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์ม

อย่าใช้ <input> ที่ซ่อนอยู่เพื่อแสดงค่าของตัวควบคุมที่กำหนดเองในแบบฟอร์ม ใช้องค์ประกอบที่กำหนดเองที่เชื่อมโยงกับแบบฟอร์ม (FACE): ด้วย formAssociated และ ElementInternals องค์ประกอบจะส่งค่าของตัวเอง เข้าร่วมในการตรวจสอบ และแสดงสถานะของแบบฟอร์มโดยค่าเริ่มต้น

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);

ข้อมูลสำคัญ

  • static formAssociated = true เป็นข้อบังคับ ซึ่งจะปลดล็อกเมธอด ElementInternals ที่เกี่ยวข้องกับแบบฟอร์มและวงจร formXxxCallback
  • setFormValue(value) คือสิ่งที่ส่ง โดยไม่ต้องป้อนข้อมูลที่ซ่อนอยู่ ส่ง FormData สำหรับการควบคุมหลายค่า
  • setValidity(flags, message, anchor) ผสานรวมกับการตรวจสอบข้อจำกัดแบบเนทีฟ: ระบบจะไม่ส่งแบบฟอร์มขณะที่ไม่มีความถูกต้อง, :invalid/:valid มีผล และองค์ประกอบ Anchor จะได้รับโฟกัสในรายงาน โทรหา setValidity({}) เพื่อเคลียร์ คลาสเทียม :user-valid/:user-invalid ที่รับรู้การโต้ตอบไม่ได้เชื่อมต่อกับองค์ประกอบที่เชื่อมโยงกับแบบฟอร์มอย่างสอดคล้องกันในเบราว์เซอร์ต่างๆ โปรดตรวจสอบการรองรับก่อนที่จะใช้คลาสเทียมเหล่านี้ แทนที่จะถือว่าคลาสเทียมเหล่านี้ติดตามการโต้ตอบของผู้ใช้ในลักษณะเดียวกับการควบคุมแบบเนทีฟ
  • ใช้ formResetCallback, formDisabledCallback และ formStateRestoreCallback เพื่อให้องค์ประกอบทำงานเหมือนกับการควบคุมแบบเนทีฟเมื่อรีเซ็ต fieldset[disabled] และกู้คืนการป้อนข้อความอัตโนมัติเมื่อย้อนกลับ/ไปข้างหน้า ใน formDisabledCallback(disabled) ให้ปิดใช้การควบคุมภายใน (หรือนำออกจากลำดับแท็บและละเว้นอินพุต) แทนที่จะตั้งค่า disabled ในโฮสต์ การตั้งค่าแอตทริบิวต์โฮสต์ภายใน Callback จะป้องกันไม่ให้เปิดใช้อีกครั้งเมื่อเปิดใช้ <fieldset> ระดับบนอีกครั้ง และโฮสต์ตรงกับ :disabled โดยอัตโนมัติอยู่แล้ว
  • ปฏิบัติตามข้อตกลงของแพลตฟอร์มสำหรับเหตุการณ์ที่ส่ง และเลือกใช้เหตุการณ์ input และ change ในตัวมากกว่าเหตุการณ์ที่กำหนดเอง input จะเริ่มทำงานเมื่อค่าเปลี่ยนแปลงอันเป็นผลโดยตรงจากการกระทำของผู้ใช้ (การพิมพ์ การลากแถบเลื่อน) change จะเริ่มทำงาน 1 ครั้งต่อสตรีมการเปลี่ยนแปลงเมื่อผู้ใช้ดำเนินการ (การปล่อยแถบเลื่อน การเบลอฟิลด์) สร้างด้วย InputEvent และ Event ตามลำดับ

กลยุทธ์สำรอง

ตรวจหาฟีเจอร์ด้วยการรองรับ static formAssociated ก่อนที่จะใช้ฟีเจอร์ดังกล่าว: 'attachInternals' in HTMLElement.prototype && 'setFormValue' in ElementInternals.prototype ในกรณีที่ใช้ไม่ได้ ให้เปลี่ยนไปใช้ <input> แบบเนทีฟที่ซ่อนไว้ซึ่งซิงค์กับค่าของตัวควบคุมเพื่อให้แบบฟอร์มยังคงส่งได้ ซึ่งเป็นกรณีเดียวที่รูปแบบอินพุตที่ซ่อนไว้มีความเหมาะสม