Now.js Framework Documentation

Now.js Framework Documentation

CascadingSelectManager

TH 15 Dec 2025 08:52

CascadingSelectManager

ภาพรวม

CascadingSelectManager คือระบบจัดการ cascading/dependent select boxes ใน Now.js Framework โหลด options แบบ dynamic ตาม selection ก่อนหน้า

ใช้เมื่อ:

  • Province → District → Subdistrict selects
  • Category → Subcategory → Product selects
  • Country → State → City selects
  • Any dependent dropdown chains

ทำไมต้องใช้:

  • ✅ AJAX loading พร้อม debounce
  • ✅ Auto-disable empty selects
  • ✅ Supports form integration
  • ✅ Default option customization
  • ✅ Auto-initialization from HTML

การใช้งานพื้นฐาน

HTML Declarative

<form id="address-form">
  <!-- Parent select -->
  <select name="province"
          data-cascade-url="/api/provinces"
          data-cascade-children="district"
          data-cascade-placeholder="เลือกจังหวัด">
    <option value="">โปรดเลือก...</option>
  </select>

  <!-- Child select (depends on province) -->
  <select name="district"
          data-cascade-url="/api/districts"
          data-cascade-parent="province"
          data-cascade-children="subdistrict"
          data-cascade-placeholder="เลือกอำเภอ"
          disabled>
    <option value="">โปรดเลือก...</option>
  </select>

  <!-- Grandchild select (depends on district) -->
  <select name="subdistrict"
          data-cascade-url="/api/subdistricts"
          data-cascade-parent="district"
          data-cascade-placeholder="เลือกตำบล"
          disabled>
    <option value="">โปรดเลือก...</option>
  </select>
</form>

JavaScript API

// Initialize in container
CascadingSelectManager.initInContainer(document.getElementById('address-form'));

// Or create manually
CascadingSelectManager.create([
  document.querySelector('[name="province"]'),
  document.querySelector('[name="district"]'),
  document.querySelector('[name="subdistrict"]')
], {
  autoLoad: true,
  autoDisable: true
});

Data Attributes

Attribute Description
data-cascade-url API URL สำหรับโหลด options
data-cascade-parent name ของ parent select
data-cascade-children name ของ child select(s) (comma-separated)
data-cascade-placeholder Placeholder text
data-cascade-params Additional query params (JSON)
data-cascade-value-field Field name for value (default: 'id')
data-cascade-label-field Field name for label (default: 'name')

API Response Format

// Array of objects
[
  { id: 1, name: 'Bangkok' },
  { id: 2, name: 'Chiang Mai' },
  { id: 3, name: 'Phuket' }
]

// Or custom fields
[
  { code: 'BKK', title: 'Bangkok' },
  { code: 'CNX', title: 'Chiang Mai' }
]
// Use: data-cascade-value-field="code" data-cascade-label-field="title"

Query parameters ที่ส่งไป:

  • Parent field name = parent value
  • Additional params from data-cascade-params
GET /api/districts?province=1
GET /api/subdistricts?district=101

การตั้งค่า

CascadingSelectManager.init({
  // Loading indicator class
  loadingClass: 'loading',

  // Debounce delay (ms)
  ajaxDelay: 300,

  // Add default "Please select" option
  defaultOption: true,
  defaultOptionText: 'Please select...',

  // Auto-load first select on init
  autoLoad: true,

  // Auto-disable empty selects
  autoDisable: true,

  // Custom placeholders per select
  placeholders: {
    province: 'เลือกจังหวัด',
    district: 'เลือกอำเภอ'
  },

  debug: false
});

API อ้างอิง

CascadingSelectManager.initInContainer(container)

Initialize cascading selects ใน container

Parameter Type Description
container HTMLElement Form หรือ container element

CascadingSelectManager.create(selects, options)

สร้าง cascading select chain

Parameter Type Description
selects Array Array of select elements
options object Configuration options

Returns: Object - Instance

CascadingSelectManager.reset(instance)

Reset ทุก select กลับค่าเริ่มต้น

Parameter Type Description
instance Object Instance จาก create()

CascadingSelectManager.destroy(instance)

ทำลาย instance

Parameter Type Description
instance Object Instance

CascadingSelectManager.getInstance(id)

รับ instance ตาม ID

CascadingSelectManager.getInstanceForElement(element)

รับ instance จาก select element

เหตุการณ์

Event เมื่อเกิด Detail
cascade:loading เริ่มโหลด options {select, parent}
cascade:loaded โหลดเสร็จ {select, options, count}
cascade:error โหลด error {select, error}
cascade:change Select เปลี่ยนค่า {select, value, children}
cascade:reset Reset ทั้งหมด {instance}
EventManager.on('cascade:loaded', (data) => {
  console.log(`Loaded ${data.count} options for ${data.select.name}`);
});

ตัวอย่างการใช้งานจริง

Thai Address Form

<div class="address-form" id="address-section">
  <div class="form-group">
    <label>จังหวัด</label>
    <select name="province_id"
            data-cascade-url="/api/address/provinces"
            data-cascade-children="amphur_id"
            data-cascade-value-field="id"
            data-cascade-label-field="name_th">
      <option value="">เลือกจังหวัด</option>
    </select>
  </div>

  <div class="form-group">
    <label>อำเภอ/เขต</label>
    <select name="amphur_id"
            data-cascade-url="/api/address/amphurs"
            data-cascade-parent="province_id"
            data-cascade-children="district_id"
            disabled>
      <option value="">เลือกอำเภอ</option>
    </select>
  </div>

  <div class="form-group">
    <label>ตำบล/แขวง</label>
    <select name="district_id"
            data-cascade-url="/api/address/districts"
            data-cascade-parent="amphur_id"
            data-cascade-children="zipcode"
            disabled>
      <option value="">เลือกตำบล</option>
    </select>
  </div>

  <div class="form-group">
    <label>รหัสไปรษณีย์</label>
    <select name="zipcode"
            data-cascade-url="/api/address/zipcodes"
            data-cascade-parent="district_id"
            disabled>
      <option value="">เลือกรหัสไปรษณีย์</option>
    </select>
  </div>
</div>

<script>
document.addEventListener('DOMContentLoaded', () => {
  CascadingSelectManager.initInContainer(document.getElementById('address-section'));
});
</script>

Category Filter

// Product category → Subcategory → Brand
const instance = CascadingSelectManager.create([
  categorySelect,
  subcategorySelect,
  brandSelect
], {
  autoLoad: true,
  defaultOptionText: 'ทั้งหมด'
});

// Listen for final selection
brandSelect.addEventListener('change', (e) => {
  filterProducts({
    category: categorySelect.value,
    subcategory: subcategorySelect.value,
    brand: e.target.value
  });
});

Pre-populated Values

<!-- สำหรับ edit form ที่มีค่าอยู่แล้ว -->
<select name="province" data-cascade-url="/api/provinces">
  <option value="">เลือก</option>
  <option value="1" selected>กรุงเทพฯ</option>
</select>

<select name="district"
        data-cascade-url="/api/districts"
        data-cascade-parent="province"
        data-cascade-initial-value="101">
  <option value="">เลือก</option>
</select>

ข้อควรระวัง

⚠️ 1. Parent Name ต้องตรงกัน

<!-- ❌ parent ไม่ตรง -->
<select name="province_id">...</select>
<select data-cascade-parent="province">...</select>

<!-- ✅ ต้องตรงกัน -->
<select name="province">...</select>
<select data-cascade-parent="province">...</select>

⚠️ 2. API Response Format

// ❌ Response format ไม่ถูก
{ success: true, data: [...] }

// ✅ Return array โดยตรง
[{ id: 1, name: 'Bangkok' }, ...]

// หรือ configure fields
data-cascade-value-field="id"
data-cascade-label-field="name"

⚠️ 3. Initialize หลัง DOM Ready

// ❌ ก่อน DOM ready
CascadingSelectManager.initInContainer(document.getElementById('form'));

// ✅ หลัง DOM ready
document.addEventListener('DOMContentLoaded', () => {
  CascadingSelectManager.initInContainer(document.getElementById('form'));
});

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