Cascading Select

Thai address selection with cascading dropdowns (Province → District → Subdistrict)

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.

Select Address

`
Select province first
Select province first
Select district first

How It Works

1

Form Enhancement

FormManager enhances forms with data-form attribute and registers select elements with ElementManager.

2

Setup Cascade Listeners

Child selects with data-cascade-parent listen to parent's change event.

3

Load Options on Change

When parent changes, child calls data-cascade-url with parent value as parameter.

4

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