การใช้งานร่วมกับฟอร์ม
ส่งค่าที่อยู่ไทยด้วยฟอร์มของเบราว์เซอร์ หรือเชื่อมต่อกับ 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:
'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>
)
}'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 selectaria-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