Now.js Framework Documentation
CascadingSelectManager
CascadingSelectManager
Overview
CascadingSelectManager is the cascading/dependent select box management system in Now.js Framework. It loads options dynamically based on previous selections.
When to use:
- Province → District → Subdistrict selects
- Category → Subcategory → Product selects
- Country → State → City selects
- Any dependent dropdown chains
Why use it:
- ✅ AJAX loading with debounce
- ✅ Auto-disable empty selects
- ✅ Supports form integration
- ✅ Default option customization
- ✅ Auto-initialization from HTML
Basic Usage
HTML Declarative
<form id="address-form">
<!-- Parent select -->
<select name="province"
data-cascade-url="/api/provinces"
data-cascade-children="district"
data-cascade-placeholder="Select province">
<option value="">Please select...</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="Select district"
disabled>
<option value="">Please select...</option>
</select>
<!-- Grandchild select (depends on district) -->
<select name="subdistrict"
data-cascade-url="/api/subdistricts"
data-cascade-parent="district"
data-cascade-placeholder="Select subdistrict"
disabled>
<option value="">Please select...</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 for loading options |
data-cascade-parent |
name of parent select |
data-cascade-children |
name of 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: 'New York' },
{ id: 2, name: 'Los Angeles' },
{ id: 3, name: 'Chicago' }
]
// Or custom fields
[
{ code: 'NY', title: 'New York' },
{ code: 'LA', title: 'Los Angeles' }
]
// Use: data-cascade-value-field="code" data-cascade-label-field="title"Query parameters sent:
- Parent field name = parent value
- Additional params from
data-cascade-params
GET /api/districts?province=1
GET /api/subdistricts?district=101Configuration
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: 'Select province',
district: 'Select district'
},
debug: false
});API Reference
CascadingSelectManager.initInContainer(container)
Initialize cascading selects in container
| Parameter | Type | Description |
|---|---|---|
container |
HTMLElement | Form or container element |
CascadingSelectManager.create(selects, options)
Create cascading select chain
| Parameter | Type | Description |
|---|---|---|
selects |
Array | Array of select elements |
options |
object | Configuration options |
Returns: Object - Instance
CascadingSelectManager.reset(instance)
Reset all selects to initial state
| Parameter | Type | Description |
|---|---|---|
instance |
Object | Instance from create() |
CascadingSelectManager.destroy(instance)
Destroy instance
| Parameter | Type | Description |
|---|---|---|
instance |
Object | Instance |
CascadingSelectManager.getInstance(id)
Get instance by ID
CascadingSelectManager.getInstanceForElement(element)
Get instance from select element
Events
| Event | When Triggered | Detail |
|---|---|---|
cascade:loading |
Start loading options | {select, parent} |
cascade:loaded |
Loading complete | {select, options, count} |
cascade:error |
Loading error | {select, error} |
cascade:change |
Select value changed | {select, value, children} |
cascade:reset |
All reset | {instance} |
EventManager.on('cascade:loaded', (data) => {
console.log(`Loaded ${data.count} options for ${data.select.name}`);
});Real-World Examples
Address Form
<div class="address-form" id="address-section">
<div class="form-group">
<label>Country</label>
<select name="country_id"
data-cascade-url="/api/address/countries"
data-cascade-children="state_id"
data-cascade-value-field="id"
data-cascade-label-field="name">
<option value="">Select country</option>
</select>
</div>
<div class="form-group">
<label>State</label>
<select name="state_id"
data-cascade-url="/api/address/states"
data-cascade-parent="country_id"
data-cascade-children="city_id"
disabled>
<option value="">Select state</option>
</select>
</div>
<div class="form-group">
<label>City</label>
<select name="city_id"
data-cascade-url="/api/address/cities"
data-cascade-parent="state_id"
disabled>
<option value="">Select city</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: 'All'
});
// Listen for final selection
brandSelect.addEventListener('change', (e) => {
filterProducts({
category: categorySelect.value,
subcategory: subcategorySelect.value,
brand: e.target.value
});
});Pre-populated Values
<!-- For edit form with existing values -->
<select name="country" data-cascade-url="/api/countries">
<option value="">Select</option>
<option value="1" selected>United States</option>
</select>
<select name="state"
data-cascade-url="/api/states"
data-cascade-parent="country"
data-cascade-initial-value="5">
<option value="">Select</option>
</select>Common Pitfalls
⚠️ 1. Parent Name Must Match
<!-- ❌ Parent doesn't match -->
<select name="province_id">...</select>
<select data-cascade-parent="province">...</select>
<!-- ✅ Must match exactly -->
<select name="province">...</select>
<select data-cascade-parent="province">...</select>⚠️ 2. API Response Format
// ❌ Incorrect response format
{ success: true, data: [...] }
// ✅ Return array directly
[{ id: 1, name: 'New York' }, ...]
// Or configure fields
data-cascade-value-field="id"
data-cascade-label-field="name"⚠️ 3. Initialize After DOM Ready
// ❌ Before DOM ready
CascadingSelectManager.initInContainer(document.getElementById('form'));
// ✅ After DOM ready
document.addEventListener('DOMContentLoaded', () => {
CascadingSelectManager.initInContainer(document.getElementById('form'));
});Related Documentation
- SelectElementFactory - Select inputs
- FormManager - Form handling