Features Overview
Cascading Select provides dependent dropdown selection where choosing a parent option updates child options:
🔗 Dependent Selection
Child select updates when parent changes
onChange → loadOptions
📦 Dynamic Loading
Load options from API based on parent value
get-districts.php?province=10
⌨️ Type-to-Filter
Type to quickly find options in long lists
typeToFilter: true
🏷️ Placeholder Support
Show placeholder when no selection made
data-placeholder="Select..."
📱 Responsive
Works well on mobile and desktop
Native select behavior
🔄 Auto-Clear
Child selects are cleared when parent changes
Automatic cascade reset
API Reference
Select element attributes for cascading behavior:
| Attribute | Element | Description |
|---|---|---|
data-cascade-api |
<form> |
Centralized API endpoint for all cascade updates |
data-load-api |
<form> |
API endpoint to load initial form data and options |
data-cascade-source |
<select> |
Field identifier sent to API when changed (e.g. "province") |
data-options-key |
<select> |
Key in API response options object to populate this select |
name |
string | Form field name |
data-placeholder |
string | Placeholder text (creates disabled first option) |
data-cascade-parent |
<select> |
(Legacy) Name of parent select element for direct linking |
data-cascade-url |
<select> |
(Legacy) URL to load options for individual select |
data-cascade-param |
string | Parameter name for parent value (default: parent name) |
required |
boolean | Make field required |
API Response Format
The complete address API returns current data and all available options:
{
"success": true,
"message": "Address data retrieved successfully",
"code": 200,
"data": {
"data": {
"province": "71",
"amphur": "7101",
"district": "710107",
"zipcode": "71190",
"address": "999/9 moo 9"
},
"options": {
"provinces": [
{"value": 10, "text": "กรุงเทพมหานคร"},
{"value": 11, "text": "สมุทรปราการ"},
{"value": 12, "text": "นนทบุรี"},
{"value": 71, "text": "กาญจนบุรี"}
// ... all 77 provinces
],
"amphurs": [
{"value": 7101, "text": "เมืองกาญจนบุรี"},
{"value": 7102, "text": "ไทรโยค"},
{"value": 7103, "text": "บ่อพลอย"}
// ... districts for selected province
],
"districts": [
{"value": 710101, "text": "บ้านเหนือ"},
{"value": 710102, "text": "บ้านใต้"},
{"value": 710107, "text": "ลาดหญ้า"}
// ... sub-districts for selected amphur
]
}
}
}
Cascade API Response
When a parent select changes, the cascade API returns updated data and options:
// POST /v1/address/cascade
// Body: { source: "province", province: "71", amphur: "0", district: "0" }
{
"success": true,
"message": "Cascade options loaded successfully",
"code": 200,
"data": {
"data": {
"province": "71",
"amphur": "0",
"district": "0",
"zipcode": ""
},
"options": {
"amphurs": [
{"value": 7101, "text": "เมืองกาญจนบุรี"},
{"value": 7102, "text": "ไทรโยค"}
],
"districts": []
}
}
}
Code Examples
Example 1: Centralized Cascade (Recommended)
Use a single API endpoint to handle all cascade logic. This is cleaner and easier to maintain.
<form data-form="addressForm"
data-load-api="api/v1/address"
data-cascade-api="api/v1/address/cascade">
<!-- Province -->
<select name="province"
data-cascade-source="province"
data-options-key="provinces">
<option value="" disabled selected>-- เลือกจังหวัด --</option>
</select>
<!-- District (Amphur) -->
<select name="amphur"
data-cascade-source="amphur"
data-options-key="amphurs">
<option value="" disabled selected>-- เลือกอำเภอ --</option>
</select>
<!-- Subdistrict (District) -->
<select name="district"
data-cascade-source="district"
data-options-key="districts">
<option value="" disabled selected>-- เลือกตำบล --</option>
</select>
</form>
Example 2: Each Child with Own URL
Each child can have its own cascade URL for different endpoints:
<!-- District loads from different endpoint -->
<select name="district"
data-cascade-parent="province"
data-cascade-url="api/get-districts.php">
</select>
<!-- Subdistrict loads from another endpoint -->
<select name="subdistrict"
data-cascade-parent="district"
data-cascade-url="api/get-subdistricts.php">
</select>
Example 3: JavaScript Manual Control
Programmatically control cascading selects (optional - for advanced use):
// Get CascadingSelectManager instance
const manager = CascadingSelectManager.getInstance('province');
// Trigger reload of children
manager.triggerChange();
// Access individual select elements
const districtSelect = document.querySelector('select[name="district"]');
// Or manually create cascade group
CascadingSelectManager.create({
selects: [
{ element: '#province', children: ['#district'] },
{ element: '#district', children: ['#subdistrict'] }
],
url: 'get-address.php',
method: 'GET'
});
Live Demo
Try the cascading select component:
Thai Address Selection
Select province first, then district and subdistrict will be available.
How It Works
Form Enhancement
FormManager enhances forms with data-form attribute and registers select elements with ElementManager.
Setup Cascade Listeners
Child selects with data-cascade-parent listen to parent's change event.
Load Options on Change
When parent changes, child calls data-cascade-url with parent value as parameter.
Update Child Options
API response populates child select. Further children are cleared to maintain cascade integrity.
Cascading Select vs Hierarchical Text
Choose the right component for your use case:
| Feature | Cascading Select | Hierarchical Text |
|---|---|---|
| UI Type | Native <select> dropdowns |
Autocomplete text inputs |
| Data Loading | API call per level | Single JSON file |
| Best For | Large datasets, server-side filtering | Small-medium datasets, client-side |
| Reverse Search | Not supported | Supported |
| Mobile UX | Native picker on mobile | Custom dropdown |
| Accessibility | Native support | ARIA attributes needed |