Skip to content

Dynamic Dropdowns

  • Create a simple div with classes input-style has-borders no-icon mb-5
  • Inside the div
    • A label with id that begins with text_ and class color-highlight ankur_language
    • A select element with id that begins with dropdown_ and class ankur_language
    • An option element with value default, and attributes disabled selected and style="display: none" text Your Text Here
    • An i element with a check icon class and a disabled valid color-grass-dark class
    • An i element with a times icon class and a disabled invalid color-red-dark class
<div class="input-style has-borders no-icon mb-5">
<label id="text_crop_step1" class="color-highlight ankur_language">Select Crop</label>
<select
id="dropdown_crop_step1"
data-change='{"operation" : "function", "function_name" : "selected_crop_batch"}'
data-validate='{"rule_name":"stepnext","rules":["nonDefault"],"required":true,"error_header":"Select Crop","error_text":"Please select crop to proceed","show_error":false,"show_ok":true,"language":true,"valid":false}'
class="ankur_language"
style="background-color: white !important"
>
<option value="default" disabled selected style="display: none">Select Crop</option>
</select>
<i class="fa fa-check disabled valid color-grass-dark"></i>
<i class="fa fa-times disabled invalid color-red-dark"></i>
</div>

Dynamic dropdowns in this framework automatically manage label visibility, validation states, and can be populated with data from APIs. The framework handles all state changes automatically when values are selected.


When the page loads, the framework automatically:

  • Scans all <select> elements with IDs starting with dropdown_
  • Stores their initial state in structure.dropdowns[] array
  • Captures default options, first option, and validation rules

When you call dropdown("dropdown_id"):

  • Creates a singleton instance (returns same instance if called multiple times)
  • Links to the stored data in structure.dropdowns[]
  • Sets up the first option as disabled placeholder
  • Attaches a change event listener
  • Calls updateState() to set initial label visibility

  1. User selects a value from the dropdown

  2. Change event fires → The event listener attached in constructor executes:

    this.element.addEventListener("change", () => {
    this.updateState(); // Update label visibility
    this.storedDropdown.current_selected = this.element.value; // Update stored value
    this.storedDropdown.current_selected_text = /* selected text */; // Update stored text
    dropdown_enforceRules(this.element.id); // Run validation
    if (this.onChangeCallback) {
    this.onChangeCallback(this.element.value); // Call custom callback if set
    }
    });
  3. Label Visibility Update (updateState()):

    • Checks if selected value exists AND is not the default value
    • If valid: Adds input-style-active class to parent → Label becomes visible
    • If default/empty: Removes input-style-active class → Label becomes hidden
  4. Validation Runs (dropdown_enforceRules()):

    • Checks if value matches validation rules (e.g., nonDefault, required)
    • If valid:
      • Shows green checkmark (removes .disabled from .fa-check)
      • Hides red X (adds .disabled to .fa-times)
      • Hides required message (adds .disabled to <em>)
    • If invalid:
      • Shows red X (removes .disabled from .fa-times)
      • Hides green checkmark (adds .disabled to .fa-check)
      • Shows required message (removes .disabled from <em>)
  5. Custom Change Handler (if data-change attribute exists):

    • Framework parses the data-change JSON
    • If operation is “function”, calls the specified function
    • Example: selected_crop_batch() function is called when crop dropdown changes

  • Condition: Selected value exists AND is not equal to the default first option value
  • CSS Class: Parent div has .input-style-active class
  • Visual: Label floats above the dropdown with white background
  • Condition: No value selected OR value equals default first option
  • CSS Class: Parent div does NOT have .input-style-active class
  • Visual: Label is invisible (opacity: 0, shifted right)
  • Label visibility updates automatically on every value change
  • No manual intervention needed
  • Works for both user selections and programmatic value changes

Validation Rules (from data-validate attribute):

Section titled “Validation Rules (from data-validate attribute):”
  1. required: true - Field must have a non-default value
  2. rules: ["nonDefault"] - Value cannot be the default placeholder
  3. show_ok: true - Show green checkmark when valid
  4. show_error: true - Show red X when invalid
User selects value
dropdown_enforceRules() called
Check if value is required and not default
Apply additional rules (nonDefault, specificValues, etc.)
If VALID:
- Add .input-style-active (label visible)
- Show green checkmark
- Hide red X
- Hide required message
If INVALID:
- Remove .input-style-active (label hidden if default)
- Show red X
- Hide green checkmark
- Show required message

  1. Parent dropdown changes → Triggers data-change handler
  2. Handler function executes (e.g., selected_crop_batch())
  3. Child dropdowns resetdropdown("child_id").reset()
  4. API call madeload_report() with parent’s selected value
  5. Loading state showndropdown("child_id").startLoading()
  6. On success → Options added via addOptionsBulk()
  7. Loading stoppeddropdown("child_id").stopLoading()
// User selects "Tomato" from crop dropdown
selected_crop_batch() {
// 1. Reset dependent dropdowns
dropdown("dropdown_variety_step1").reset();
// 2. Get selected value
var crop_id = dropdown("dropdown_crop_step1").getValue();
// 3. Show loading
dropdown("dropdown_variety_step1").startLoading();
// 4. Fetch data
load_report("endpoint", { crop_id }, success_callback, error_callback);
}
// On success
start_variety_list_success(response) {
// 5. Process options
var options = response.map(item => ({
value: String(item.variety_id),
text: item.variety_name
}));
// 6. Stop loading
dropdown("dropdown_variety_step1").stopLoading();
// 7. Add options
dropdown("dropdown_variety_step1").addOptionsBulk(options);
// 8. Auto-select if only one option
if (options.length == 1) {
dropdown("dropdown_variety_step1").setValue(options[0].value);
}
}

dropdown("dropdown_id").startLoading();
  • Clears all options except first
  • Shows animated “Loading…” text in first option
  • Creates pulsing dot animation
dropdown("dropdown_id").stopLoading();
  • Stops animation timer
  • Restores default first option text
  • Ready for options to be added

dropdown("dropdown_id").setValue("Tomato");
  • Finds option with matching value
  • Sets dropdown value
  • Updates stored state
  • Triggers change event → Label updates, validation runs
  • Same behavior as user selection
var value = dropdown("dropdown_id").getValue(); // Returns selected value
var text = dropdown("dropdown_id").getText(); // Returns selected text

  • default_selected - Initial selected value
  • current_selected - Current selected value (updates on change)
  • current_selected_text - Current selected text
  • default_options - Initial options from HTML
  • current_options - Current options (can be modified dynamically)
  • default_first - Initial first option (placeholder)
  • current_first - Current first option
  • All state changes are stored in structure.dropdowns[]
  • State persists across page interactions
  • reset() restores all default values

  • getValue() - Get current selected value
  • getText() - Get current selected text
  • setValue(value) - Set value and trigger change event
  • empty() - Reset to default but keep options
  • addOption(value, text, tag) - Add single option
  • addOptionsBulk(optionsArray) - Add multiple options
  • removeOption(value) - Remove specific option
  • clearOptions() - Remove all except first option
  • reset() - Restore to initial state
  • updateState() - Update label visibility
  • startLoading() - Show loading animation
  • stopLoading() - Hide loading animation
  • show() / hide() - Toggle visibility
  • enable() / disable() - Toggle disabled state

  1. First Option is Always Disabled: The first option (value=“default”) is always disabled and acts as a placeholder

  2. Label Updates Automatically: No need to manually manage label visibility - it updates on every value change

  3. Validation is Automatic: Validation runs automatically when value changes, no manual trigger needed

  4. State is Centralized: All state is stored in structure.dropdowns[], not just in DOM

  5. Singleton Pattern: dropdown(id) returns the same instance, so you can safely call it multiple times

  6. Change Events Cascade: Setting value programmatically triggers the same change event as user selection

  7. Loading State: Use startLoading() before API calls and stopLoading() after receiving data

  8. Reset Clears Dynamic Options: reset() removes all dynamically added options and restores defaults


// Initialize
dropdown("dropdown_id");
// User selects value → Label appears automatically
// Validation runs automatically
// Parent changes → Handler function
function onParentChange() {
dropdown("child_id").reset();
dropdown("child_id").startLoading();
load_report("endpoint", params, success, error);
}
// On success
function success(response) {
dropdown("child_id").stopLoading();
dropdown("child_id").addOptionsBulk(options);
}
// Add options
dropdown("dropdown_id").addOptionsBulk([
{ value: "1", text: "Option 1" },
{ value: "2", text: "Option 2" }
]);
// Set initial value
dropdown("dropdown_id").setValue("1"); // Label appears automatically

When a user selects a value:

  1. Change event fires
  2. Label visibility updates (adds/removes .input-style-active)
  3. Validation runs automatically
  4. Icons update (checkmark/X)
  5. Custom change handler executes (if defined)
  6. State is stored in structure.dropdowns[]

Everything happens automatically - you just need to:

  • Define the HTML structure
  • Initialize with dropdown(id)
  • Optionally add options dynamically
  • The framework handles the rest!