Now.js Framework Documentation
Select Elements (อิลิเมนต์ Dropdown)
Select Elements (อิลิเมนต์ Dropdown)
เอกสารสำหรับ SelectElementFactory และ MultiSelectElementFactory - components สำหรับ dropdown และ multi-select พร้อม type-to-filter, Ajax loading และ keyboard navigation
📋 สารบัญ
ภาพรวม
Select elements ปรับปรุง <select> elements พื้นฐานพร้อมฟีเจอร์เพิ่มเติมขณะรักษาความเข้ากันได้กับ form
| Element | data-element | คำอธิบาย |
|---|---|---|
| SelectElementFactory | select |
Enhanced select พร้อม type-to-filter |
| MultiSelectElementFactory | select-multiple |
Custom multi-select พร้อม checkbox UI |
คุณสมบัติหลัก
- ✅ Type-to-Filter: ข้ามไปยัง options ด้วยการพิมพ์
- ✅ Ajax Loading: โหลด options จาก API
- ✅ OptGroups: รองรับ grouped options
- ✅ Keyboard Navigation: รองรับ keyboard เต็มรูปแบบ
- ✅ i18n Support: แปลภาษา option labels อัตโนมัติ
- ✅ Custom UI: MultiSelect พร้อม checkbox list
SelectElementFactory
SelectElementFactory ปรับปรุง <select> elements พื้นฐานด้วย type-to-filter, การโหลด options แบบไดนามิก และการเข้าถึงที่ดีขึ้น
การใช้งานพื้นฐาน
<!-- Simple select -->
<select data-element="select" name="country">
<option value="">เลือกประเทศ</option>
<option value="TH">ประเทศไทย</option>
<option value="US">สหรัฐอเมริกา</option>
<option value="JP">ญี่ปุ่น</option>
</select>
<!-- Select พร้อม placeholder -->
<select data-element="select"
data-placeholder="เลือกตัวเลือก"
name="category">
<option value="electronics">อิเล็กทรอนิกส์</option>
<option value="clothing">เสื้อผ้า</option>
</select>HTML Attributes
| Attribute | ประเภท | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
data-element |
string | - | ตั้งค่าเป็น select |
data-placeholder |
string | - | ข้อความ placeholder (option แรกที่ disabled) |
data-type-to-filter |
boolean | true |
เปิด type-to-filter |
data-search-method |
string | 'prefix' |
วิธีค้นหา: prefix, contains, fuzzy |
data-search-reset-delay |
number | 1000 |
รีเซ็ตคำค้นหลังจาก (ms) |
data-show-search-status |
boolean | false |
แสดงตัวบอกสถานะการค้นหา |
data-use-opt-groups |
boolean | false |
เปิดใช้ option groups |
data-allow-placeholder-selection |
boolean | false |
อนุญาตให้เลือก placeholder ได้ |
data-url |
string | - | URL สำหรับโหลด options |
data-cache |
boolean | false |
เปิด cache สำหรับคำขอ GET จาก data-url |
data-cache-time |
number | 60000 |
อายุ cache ของ data-url หน่วยมิลลิวินาที |
data-value |
string | - | ค่าเริ่มต้นหลังโหลด options |
data-options-key |
string | - | Key สำหรับ options จาก parent data |
multiple |
boolean | false |
เปิดใช้การเลือกหลายรายการ |
size |
number | 1 |
จำนวน options ที่มองเห็น |
เมื่อกำหนด data-cache="true" การโหลด data-url ซ้ำด้วย query parameters ชุดเดิมจะใช้ option payload จาก cache จนกว่า TTL จะหมด
เมื่อ TemplateManager เป็นคนขับ data-options-key, key ที่ resolve สามารถ reactive ได้และอาจมี dot path, bracket path หรือ {{ }} interpolation ได้ ดูเพิ่มที่ ../template/data-options-key.md
Configuration Object
SelectElementFactory.config = {
multiple: false,
size: 1,
typeToFilter: true,
searchMethod: 'prefix', // 'prefix', 'contains', 'fuzzy'
searchResetDelay: 1000, // ms
showSearchStatus: false,
useOptGroups: false,
allowPlaceholderSelection: false,
optimizeRendering: true,
ariaLabelledBy: null,
emptyMessage: 'No options available',
customTemplate: null,
validationMessages: {
required: 'Please select an option'
}
};Type-to-Filter
เมื่อ focus ที่ select ผู้ใช้สามารถพิมพ์เพื่อข้ามไปยัง options ที่ตรงกัน:
วิธีค้นหา:
| วิธี | คำอธิบาย | ตัวอย่าง |
|---|---|---|
prefix |
ตรงจากตัวแรก | "กร" → "กรุงเทพ" |
contains |
ตรงที่ไหนก็ได้ | "เทพ" → "กรุงเทพ" |
fuzzy |
ตรงตัวอักษรตามลำดับ | "กท" → "กรุงเทพ" |
<!-- ค้นหาแบบ contains -->
<select data-element="select"
data-search-method="contains"
name="country">
<!-- "เทพ" จะตรงกับ กรุงเทพ -->
</select>โหลด Options จาก URL
<select data-element="select"
data-url="/api/countries"
data-value="TH"
name="country">
<option value="">เลือกประเทศ</option>
</select>Expected API Response:
{
"success": true,
"data": [
{"value": "TH", "text": "ประเทศไทย"},
{"value": "US", "text": "สหรัฐอเมริกา"}
]
}โหลด Options จาก Parent Data
เมื่อใช้ใน modal หรือ form ที่มี parent data:
<select data-element="select"
data-options-key="countries"
name="country">
<option value="">เลือกประเทศ</option>
</select>ถ้าต้องการ restore ค่าที่ selected ด้วย ให้ใช้ data-options-key คู่กับ data-attr="value:..." หรือ data-model
// เติมค่าจาก options object
SelectElementFactory.populateFromOptions(selectElement, {
countries: [
{value: 'TH', text: 'ประเทศไทย'},
{value: 'US', text: 'สหรัฐอเมริกา'}
]
}, 'countries');
// หรือเติมค่าให้ select ทุกตัวใน container
SelectElementFactory.populateFromOptionsInContainer(formElement, optionsData);Option Groups
<select data-element="select"
data-use-opt-groups="true"
name="region">
<option value="">เลือกภูมิภาค</option>
</select>instance.updateOptions([
{
label: 'เอเชีย',
options: [
{value: 'TH', text: 'ประเทศไทย'},
{value: 'JP', text: 'ญี่ปุ่น'}
]
},
{
label: 'ยุโรป',
options: [
{value: 'DE', text: 'เยอรมนี'},
{value: 'FR', text: 'ฝรั่งเศส'}
]
}
]);JavaScript API
const instance = ElementManager.getInstance(selectElement);
// รับค่าปัจจุบัน
console.log(instance.value); // 'TH'
console.log(instance.selectedOption); // {value: 'TH', text: 'ประเทศไทย', index: 1}
// ตั้งค่า
instance.value = 'US';
// รับ options ทั้งหมด
console.log(instance.options);
// [{value: '', text: 'เลือก...', disabled: true}, {value: 'TH', ...}]
// อัพเดท options แบบไดนามิก
instance.updateOptions([
{value: '1', text: 'ตัวเลือก 1'},
{value: '2', text: 'ตัวเลือก 2', selected: true},
{value: '3', text: 'ตัวเลือก 3', disabled: true}
]);
// โหลด options จาก URL
await instance.loadOptions('/api/cities', {country: 'TH'});
// ล้าง options (เก็บ placeholder)
instance.clearOptions();
// เลือกด้วย value
instance.selectByValue('TH');
// เลือกด้วย index
instance.selectByIndex(2);
// อัพเดท option ที่ระบุ
instance.updateOptionAt(1, {
text: 'ข้อความใหม่',
disabled: true
});Property Handlers
SelectElementFactory.propertyHandlers = {
value: {
get(element) { /* คืนค่า string หรือ array กรณี multiple */ },
set(instance, newValue) { /* ตั้งค่าการเลือก */ }
},
options: {
get(element) { /* คืนค่า array ของ option objects */ },
set(instance, newValue) { /* อัพเดท options */ }
},
selectedOption: {
get(element) { /* คืนรายละเอียดของ option ที่เลือก */ },
set(instance, newValue) { /* ตั้งค่าการเลือกด้วย option object */ }
}
};MultiSelectElementFactory
MultiSelectElementFactory แปลง <select multiple> เป็น custom UI พร้อมปุ่ม trigger และ dropdown panel พร้อม checkboxes
การใช้งานพื้นฐาน
<select data-element="select-multiple"
multiple
name="categories[]">
<option value="1">อิเล็กทรอนิกส์</option>
<option value="2">เสื้อผ้า</option>
<option value="3">หนังสือ</option>
<option value="4">กีฬา</option>
</select>UI ที่แสดง:
- ซ่อน select ต้นฉบับ
- ปุ่ม trigger แสดงรายการที่เลือก
- Dropdown panel พร้อม check/uncheck icons
HTML Attributes
| Attribute | ประเภท | ค่าเริ่มต้น | คำอธิบาย |
|---|---|---|---|
data-element |
string | - | ตั้งค่าเป็น select-multiple |
data-placeholder |
string | - | ข้อความเมื่อไม่มีการเลือก |
data-max-display-items |
number | 2 |
จำนวนสูงสุดที่แสดงก่อน "+n items" |
multiple |
boolean | required | ต้องตั้งค่า |
required |
boolean | false |
ต้องเลือกอย่างน้อย 1 รายการ |
Configuration Object
MultiSelectElementFactory.config = {
maxDisplayItems: 2,
emptyMessage: 'No options available',
validationMessages: {
required: 'Please select at least one option'
}
};รูปแบบการแสดง
ขึ้นกับการตั้งค่า maxDisplayItems:
| จำนวนที่เลือก | maxDisplayItems | แสดง |
|---|---|---|
| 0 | - | "เลือกหมวดหมู่" (placeholder) |
| 1 | 2 | "อิเล็กทรอนิกส์" |
| 2 | 2 | "อิเล็กทรอนิกส์, เสื้อผ้า" |
| 3 | 2 | "อิเล็กทรอนิกส์, เสื้อผ้า +1 items" |
| 5 | 2 | "อิเล็กทรอนิกส์, เสื้อผ้า +3 items" |
Keyboard Navigation
| ปุ่ม | การทำงาน |
|---|---|
Enter / Space |
เปิด/ปิด dropdown |
↓ |
ไปยัง option ถัดไป |
↑ |
ไปยัง option ก่อนหน้า |
Space |
สลับการเลือกรายการที่ highlight |
Escape / Tab |
ปิด dropdown |
JavaScript API
const instance = ElementManager.getInstance(selectElement);
// รับค่าที่เลือก
console.log(instance.getValue()); // ['1', '3']
console.log(instance.value); // ['1', '3']
// รับ options ที่เลือกพร้อมรายละเอียด
console.log(instance.selectedOptions);
// [{value: '1', text: 'อิเล็กทรอนิกส์'}, {value: '3', text: 'หนังสือ'}]
// ตั้งค่า
instance.setValue(['1', '2', '4']);
// ล้างการเลือก
instance.clear();
// อัพเดท options
instance.updateOptions([
{value: '1', text: 'ตัวเลือกใหม่ 1'},
{value: '2', text: 'ตัวเลือกใหม่ 2'}
]);การจัดรูปแบบ (Styling)
/* Trigger button */
.dropdown-button {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
border: 1px solid var(--color-border);
border-radius: var(--border-radius);
background: var(--color-background);
cursor: pointer;
}
.dropdown-button.disabled {
opacity: 0.6;
cursor: not-allowed;
}
/* ส่วนแสดงรายการที่เลือก */
.dropdown-display {
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.dropdown-display.placeholder {
color: var(--color-text-muted);
}
/* ไอคอนลูกศร */
.dropdown-arrow {
margin-left: 8px;
}
/* รายการใน dropdown พร้อม check icons */
.autocomplete-list li .icon-check,
.autocomplete-list li .icon-uncheck {
margin-right: 8px;
}โหลด Options จาก Parent Data
<select data-element="select-multiple"
data-options-key="permissions"
multiple
name="permissions[]">
</select>// เติมค่าจาก options object
MultiSelectElementFactory.populateFromOptions(selectElement, {
permissions: [
{value: 'read', text: 'อ่าน'},
{value: 'write', text: 'เขียน'},
{value: 'delete', text: 'ลบ'}
]
}, 'permissions');
// หรือเติมค่าให้ multi-select ทุกตัวใน container
MultiSelectElementFactory.populateFromOptionsInContainer(formElement, optionsData);เหตุการณ์
เหตุการณ์ของ SelectElementFactory
| Event | เมื่อเกิด | Detail |
|---|---|---|
change |
เลือกเปลี่ยน | Native event |
optionschanged |
อัพเดท options | {options} |
locale:changed |
เปลี่ยนภาษา | อัพเดทคำแปลแล้ว |
selectElement.addEventListener('change', (e) => {
const instance = ElementManager.getInstance(e.target);
console.log('เลือก:', instance.selectedOption);
});
selectElement.addEventListener('optionschanged', (e) => {
console.log('Options ใหม่:', e.detail.options);
});เหตุการณ์ของ MultiSelectElementFactory
| Event | เมื่อเกิด | Detail |
|---|---|---|
change |
เลือกเปลี่ยน | Native event บน select ต้นฉบับ |
selectElement.addEventListener('change', (e) => {
const instance = ElementManager.getInstance(e.target);
console.log('ค่าที่เลือก:', instance.getValue());
});แนวทางปฏิบัติที่ดี
1. ✅ ใส่ Placeholder เสมอ
<!-- ✅ ดี: บอกชัดว่าต้องเลือก -->
<select data-element="select" name="country">
<option value="">เลือกประเทศ</option>
<option value="TH">ประเทศไทย</option>
</select>
<!-- ❌ ไม่ดี: ไม่บอกว่าต้องทำอะไร -->
<select data-element="select" name="country">
<option value="TH">ประเทศไทย</option>
</select>2. ✅ ใช้ data-options-key สำหรับ Modal Forms
<!-- ✅ ดี: Options โหลดจาก modal data -->
<form data-form="user-edit">
<select data-element="select"
data-options-key="roles"
name="role_id">
</select>
</form>
<!-- Modal response ส่ง options มาให้ -->
<script>
// Modal response: {options: {roles: [{value: 1, text: 'Admin'}, ...]}}
</script>3. ✅ ใช้ Contains Search สำหรับรายการยาว
<!-- ✅ ดี: ผู้ใช้ค้นหาจากตรงไหนก็ได้ -->
<select data-element="select"
data-search-method="contains"
name="country">
<!-- 200+ ประเทศ -->
</select>
<!-- ❌ เหมาะสมน้อยกว่า: ค้นหาได้เฉพาะตัวขึ้นต้น -->
<select data-element="select"
data-search-method="prefix"
name="country">
</select>4. ✅ ตั้งค่าเริ่มต้นด้วย data-value
<!-- ✅ ดี: ตั้งค่าหลังโหลด options เสร็จ -->
<select data-element="select"
data-url="/api/countries"
data-value="TH"
name="country">
</select>
<!-- ❌ ไม่ดี: ค่าอาจหายระหว่างโหลด options -->
<select data-element="select"
data-url="/api/countries"
value="TH"
name="country">
</select>5. ✅ ใช้ Array Notation สำหรับ MultiSelect Names
<!-- ✅ ดี: Server ได้รับ array -->
<select data-element="select-multiple"
multiple
name="categories[]">
</select>
<!-- Form submission: categories[]=1&categories[]=3 -->6. ✅ ตรวจสอบการเลือกที่จำเป็น
<!-- ✅ ดี: บังคับให้เลือกอย่างน้อย 1 รายการ -->
<select data-element="select-multiple"
multiple
required
name="permissions[]">
</select>7. ✅ ใช้ OptGroups สำหรับ Options ที่จัดหมวดหมู่
<!-- ✅ ดี: จัดกลุ่มให้อ่านง่าย -->
<select data-element="select" data-use-opt-groups="true">
<optgroup label="เอเชีย">
<option value="TH">ประเทศไทย</option>
</optgroup>
<optgroup label="ยุโรป">
<option value="DE">เยอรมนี</option>
</optgroup>
</select>เอกสารที่เกี่ยวข้อง
- Form Elements ภาพรวม
- ElementFactory - Base class
- Text Elements - Text inputs พร้อม autocomplete
- FormManager - Form-level management
- ModalManager - Modal forms พร้อม options