Now.js Framework Documentation

Now.js Framework Documentation

Select Elements (อิลิเมนต์ Dropdown)

TH 04 Sep 2026 01:40

Select Elements (อิลิเมนต์ Dropdown)

เอกสารสำหรับ SelectElementFactory และ MultiSelectElementFactory - components สำหรับ dropdown และ multi-select พร้อม type-to-filter, Ajax loading และ keyboard navigation

📋 สารบัญ

  1. ภาพรวม
  2. SelectElementFactory
  3. MultiSelectElementFactory
  4. Events
  5. แนวทางปฏิบัติที่ดี

ภาพรวม

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>
<!-- ✅ ดี: ผู้ใช้ค้นหาจากตรงไหนก็ได้ -->
<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>

เอกสารที่เกี่ยวข้อง