react-thaizip

การใช้งานร่วมกับฟอร์ม

ส่งค่าที่อยู่ไทยด้วยฟอร์มของเบราว์เซอร์ หรือเชื่อมต่อกับ state ของไลบรารีฟอร์ม

คอมโพเนนต์ที่ CLI สร้างเป็น element ของฟอร์มในเบราว์เซอร์ปกติ ไม่ได้ส่งข้อมูลหรือเก็บข้อมูลให้คุณเอง เมื่อใส่ name คอมโพเนนต์จะสร้าง hidden inputs เพื่อให้ FormData และการ submit ตามปกติอ่านค่าที่อยู่ได้

ส่งด้วยฟอร์มของเบราว์เซอร์

ตั้ง name="address" เพื่อให้ได้ฟิลด์สี่ช่องนี้:

address-subdistrict
address-district
address-province
address-zipcode

หลังเลือกที่อยู่แล้ว new FormData(form) มี payload รูปแบบนี้ โดยชื่อพื้นที่เป็นชื่อภาษาไทยของข้อมูลที่เลือก แม้ UI จะแสดงภาษาอังกฤษ:

{
  'address-subdistrict': 'คลองตันเหนือ',
  'address-district': 'วัฒนา',
  'address-province': 'กรุงเทพมหานคร',
  'address-zipcode': '10110',
}

ตัวอย่างนี้ดัก submit ไว้ อ่าน FormData และแสดงผลในหน้านี้ จึงใช้งานได้โดยไม่ต้องมี server:

FormData ที่ส่ง

null
'use client'

import { useRef, useState, type FormEvent } from 'react'
import { ThaiAddressAutocomplete } from '@/components/thai-address-autocomplete'

export default function FormAutocomplete() {
  const ref = useRef<HTMLInputElement>(null)
  const [payload, setPayload] = useState<Record<string, FormDataEntryValue> | null>(null)

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setPayload(Object.fromEntries(new FormData(event.currentTarget)))
  }

  return (
    <form className="flex flex-col gap-3" onSubmit={handleSubmit}>
      <ThaiAddressAutocomplete
        ref={ref}
        name="address"
        required
      />
      <div className="flex gap-2">
        <button type="button" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent" onClick={() => ref.current?.focus()}>
          โฟกัสช่องที่อยู่
        </button>
        <button type="submit" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent">
          ส่งฟอร์ม
        </button>
      </div>
      <p className="text-sm font-medium">FormData ที่ส่ง</p>
      <pre className="max-h-72 overflow-auto rounded-md border bg-muted p-3 text-xs">{JSON.stringify(payload, null, 2)}</pre>
    </form>
  )
}
จังหวัด
อำเภอ/เขต
ตำบล/แขวง
รหัสไปรษณีย์

FormData ที่ส่ง

null
'use client'

import { useRef, useState, type FormEvent } from 'react'
import { ThaiAddressCascadeSelect } from '@/components/thai-address-cascade-select'

export default function FormCascadeSelect() {
  const ref = useRef<HTMLButtonElement>(null)
  const [payload, setPayload] = useState<Record<string, FormDataEntryValue> | null>(null)

  function handleSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()
    setPayload(Object.fromEntries(new FormData(event.currentTarget)))
  }

  return (
    <form className="flex flex-col gap-3" onSubmit={handleSubmit}>
      <ThaiAddressCascadeSelect
        ref={ref}
        name="address"
        required
      />
      <div className="flex gap-2">
        <button type="button" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent" onClick={() => ref.current?.focus()}>
          โฟกัสช่องที่อยู่
        </button>
        <button type="submit" className="rounded-md border border-input bg-background px-3 py-1.5 text-sm hover:bg-accent">
          ส่งฟอร์ม
        </button>
      </div>
      <p className="text-sm font-medium">FormData ที่ส่ง</p>
      <pre className="max-h-72 overflow-auto rounded-md border bg-muted p-3 text-xs">{JSON.stringify(payload, null, 2)}</pre>
    </form>
  )
}

ทั้ง autocomplete และ cascade select สร้างฟิลด์ชื่อเดียวกัน หากส่ง disabled ควบคู่กัน hidden inputs ทั้งสี่ช่องก็จะถูก disabled และจะไม่ปรากฏใน FormData

ใช้กับไลบรารีฟอร์ม

เก็บ ResolvedThaiAddress | null ไว้ใน state ของฟอร์มหรือ controller ของไลบรารีที่ใช้ แล้วส่งกลับเข้าคอมโพเนนต์ผ่าน value สำหรับ autocomplete การจัดการแบบ controlled ทำให้ validation, reset และการ submit ของฟอร์มมีแหล่งข้อมูลเดียวกัน

const [address, setAddress] = useState<ResolvedThaiAddress | null>(null)

<ThaiAddressAutocomplete
  name="address"
  value={address}
  onValueChange={setAddress}
/>

สำหรับ ThaiAddressCascadeSelect ให้ใช้ค่าในฟอร์มกับ resolved address ที่เลือกครบเท่านั้น cascade select จัดการการเลือกจังหวัด/อำเภอที่ยังไม่ครบภายใน และจะไม่ส่ง resolved value จนกว่าจะเลือกครบทั้งสามระดับ การส่ง value={null} จะล้าง selection ที่เลือกครบอยู่ แต่ตั้งใจคงการเลือกบางส่วนที่กำลังดำเนินอยู่ไว้ ดังนั้น state ของฟอร์มจึงไม่ใช่แหล่งข้อมูลเดียวของ UI การเลือกบางส่วนนี้

เมื่อ reset ฟอร์มตามปกติ ให้ส่ง null เพื่อล้างที่อยู่ที่เลือกครบซึ่งเก็บไว้ หาก reset ต้องล้างการเลือกบางส่วนของ cascade ด้วย ให้ remount cascade โดยตั้งใจด้วยการเพิ่มค่า reset key:

<ThaiAddressCascadeSelect key={resetVersion} name="address" value={address} onValueChange={setAddress} />

การตรวจสอบและการเข้าถึง

  • required ส่งต่อไปยัง input ของ autocomplete หรือ select ทั้งสามของ cascade select
  • aria-invalid ระบุสถานะไม่ผ่าน validation: autocomplete ส่งให้ input และ cascade select ส่งให้ trigger ทั้งสาม
  • onBlur ของ autocomplete ทำงานเมื่อ input สูญเสียโฟกัส ส่วนของ cascade select ทำงานเมื่อ province trigger สูญเสียโฟกัส
  • ref ของ autocomplete ชี้ไปที่ HTMLInputElement; ref ของ cascade select ชี้ไปที่ปุ่ม province trigger จึงใช้สำหรับ focus จาก logic ของฟอร์มได้

ตัวอย่างเช่น ส่ง aria-invalid={Boolean(error)} ตาม validation state ของฟอร์ม และเรียก ref.current?.focus() เพื่อพาผู้ใช้กลับไปยัง control หลักที่ต้องแก้ไข

คู่มือที่เกี่ยวข้อง

ดู autocomplete สำหรับ UI ค้นหาจากข้อความ, cascade select สำหรับการเลือกตามลำดับ และ การปรับแต่ง สำหรับข้อความและ class slots

On this page