This example demonstrates how to implement cascading SelectBoxes in the following scenarios:
- Standalone SelectBoxes
- SelectBoxes in Form
When you select a value from the first SelectBox, the second SelectBox loads the filtered values. In this example, only cities from the selected state appear in the second SelectBox.
To implement this functionality, get the value from the first editor and filter the dataSource by this value in the second editor.
Files to Review
- jQuery
- Angular
- Vue
- React
- NetCore
Documentation
More Examples
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
HTML<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script type="text/javascript" src="https://cdn3.devexpress.com/jslib/19.2.6/js/dx.all.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/19.2.6/css/dx.common.css" />
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/19.2.6/css/dx.light.css" />
<script>
$(function(){
$("#stateSelectBox").dxSelectBox({
dataSource: states,
valueExpr: "ID",
displayExpr: "Name",
showClearButton: true,
onValueChanged: function(e) {
let dataSource = citySelectBox.getDataSource();
dataSource.filter("StateID", "=", e.value);
dataSource.load();
citySelectBox.option("value", null);
}
});
let citySelectBox = $("#citySelectBox").dxSelectBox({
dataSource: cities,
valueExpr: "ID",
displayExpr: "Name"
}).dxSelectBox("instance");
$("#stateCityForm").dxForm({
formData: address,
onFieldDataChanged: function(e) {
if(e.dataField === "StateID") {
let cityEditor = e.component.getEditor("CityID");
cityEditor.getDataSource().filter(['StateID', '=', e.value]);
cityEditor.getDataSource().load();
e.component.updateData("CityID", null);
}
},
items:[
// State item
{
label: { text: "State" },
dataField: "StateID",
editorType: "dxSelectBox",
editorOptions: {
dataSource: states,
valueExpr: "ID",
displayExpr: "Name",
showClearButton: true
}
},
// City item
{
label: { text: "City" },
dataField: "CityID",
editorType: "dxSelectBox",
editorOptions: {
dataSource: cities,
valueExpr: "ID",
displayExpr: "Name"
}
}
]
});
});
var address = { StateID: null, CityID: null }
var states = [{
"ID": 1,
"Name": "Alabama"
}, {
"ID": 2,
"Name": "Alaska"
}, {
"ID": 3,
"Name": "Arizona"
}, {
"ID": 4,
"Name": "Arkansas"
}, {
"ID": 5,
"Name": "California"
}];
var cities = [{
"ID": 1,
"Name": "Tuscaloosa",
"StateID": 1
}, {
"ID": 2,
"Name": "Hoover",
"StateID": 1
}, {
"ID": 3,
"Name": "Dothan",
"StateID": 1
}, {
"ID": 4,
"Name": "Decatur",
"StateID": 1
}, {
"ID": 5,
"Name": "Anchorage",
"StateID": 2
}, {
"ID": 6,
"Name": "Fairbanks",
"StateID": 2
}, {
"ID": 7,
"Name": "Juneau",
"StateID": 2
}, {
"ID": 8,
"Name": "Avondale",
"StateID": 3
}, {
"ID": 9,
"Name": "Buckeye",
"StateID": 3
}, {
"ID": 10,
"Name": "Carefree",
"StateID": 3
}, {
"ID": 11,
"Name": "Springdale",
"StateID": 4
}, {
"ID": 12,
"Name": "Rogers",
"StateID": 4
}, {
"ID": 13,
"Name": "Sherwood",
"StateID": 4
}, {
"ID": 14,
"Name": "Jacksonville",
"StateID": 4
}, {
"ID": 15,
"Name": "Cabot",
"StateID": 4
}, {
"ID": 16,
"Name": "Adelanto",
"StateID": 5
}, {
"ID": 17,
"Name": "Glendale",
"StateID": 5
}, {
"ID": 18,
"Name": "Moorpark",
"StateID": 5
}, {
"ID": 19,
"Name": "Needles",
"StateID": 5
}, {
"ID": 20,
"Name": "Ontario",
"StateID": 5
}];
</script>
</head>
<body>
<div class="dx-viewport demo-container">
<div class="dx-fieldset" style="width:50%">
<div class="dx-fieldset-header">Standalone editors</div>
<div class="dx-field">
<div class="dx-field-label">State</div>
<div class="dx-field-value">
<div id="stateSelectBox"></div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">City</div>
<div class="dx-field-value">
<div id="citySelectBox"></div>
</div>
</div>
<div class="dx-fieldset-header">In Form</div>
<div class="dx-field">
<div id="stateCityForm"></div>
</div>
</div>
</div>
</body>
</html>
HTML<div class="dx-fieldset" style="width:50%">
<div class="dx-fieldset-header">Standalone editors</div>
<div class="dx-field">
<div class="dx-field-label">State</div>
<div class="dx-field-value">
<dx-select-box
[dataSource]="states"
displayExpr="Name"
valueExpr="ID"
(onValueChanged)="onStateChanged($event)"
[showClearButton]="true"
></dx-select-box>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">City</div>
<div class="dx-field-value">
<dx-select-box
#citySelectBox
displayExpr="Name"
valueExpr="ID"
[(value)]="selectedCityID"
[dataSource]="cities"
></dx-select-box>
</div>
</div>
<div class="dx-fieldset-header">In Form</div>
<div class="dx-field">
<dx-form
[(formData)]="address"
(onFieldDataChanged)="onFieldDataChanged($event)"
>
<dxi-item
dataField="StateID"
editorType="dxSelectBox"
[editorOptions]='{
dataSource: states,
valueExpr: "ID",
displayExpr: "Name",
showClearButton: true }'
>
<dxo-label text="State"></dxo-label>
</dxi-item>
<dxi-item
dataField="CityID"
editorType="dxSelectBox"
[editorOptions]='{
dataSource: cities,
valueExpr: "ID",
displayExpr: "Name" }'
>
<dxo-label text="City"></dxo-label>
</dxi-item>
</dx-form>
</div>
</div>
TypeScriptimport { Component, ViewChild } from "@angular/core";
import { DxSelectBoxComponent } from "devextreme-angular/ui/select-box";
import { State, City, Address, Service } from "./app.service";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
providers: [Service]
})
export class AppComponent {
@ViewChild("citySelectBox", { static: false })
citySelectBoxComponent: DxSelectBoxComponent;
states: State[];
cities: City[];
selectedCityID: number;
address: Address;
constructor(service: Service) {
this.states = service.getStates();
this.cities = service.getCities();
this.address = service.getAddress();
}
onStateChanged(e) {
let dataSource = this.citySelectBoxComponent.instance.getDataSource();
dataSource.filter(["StateID", "=", e.value]);
dataSource.load();
this.selectedCityID = null;
}
onFieldDataChanged(e) {
if (e.dataField === "StateID") {
var cityEditor = e.component.getEditor("CityID");
let dataSource = cityEditor.getDataSource();
dataSource.filter(["StateID", "=", e.value]);
dataSource.load();
this.address.CityID = null;
}
}
}
Code<template>
<div>
<div class="dx-fieldset" style="width:50%">
<div class="dx-fieldset-header">Select State, City</div>
<div class="dx-field">
<div class="dx-field-label">State</div>
<div class="dx-field-value">
<DxSelectBox
:data-source="states"
value-expr="ID"
display-expr="Name"
@valueChanged="stateValueChanged"
/>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">City</div>
<div class="dx-field-value">
<DxSelectBox
:ref="citySelectBoxRefKey"
:data-source="cities"
:value.sync="cityValue"
value-expr="ID"
display-expr="Name"
/>
</div>
</div>
<div class="dx-fieldset-header">In Form</div>
<div class="dx-field">
<DxForm :form-data.sync="address" @field-data-changed="fieldDataChanged">
<DxSimpleItem
data-field="StateID"
editor-type="dxSelectBox"
:editor-options="stateEditorOptions"
>
<DxLabel text="State"></DxLabel>
</DxSimpleItem>
<DxSimpleItem
data-field="CityID"
editor-type="dxSelectBox"
:editor-options="cityEditorOptions"
>
<DxLabel text="City"></DxLabel>
</DxSimpleItem>
</DxForm>
</div>
</div>
</div>
</template>
<script>
import { DxSelectBox } from "devextreme-vue";
import DxForm, { DxSimpleItem, DxLabel } from "devextreme-vue/form";
import service from "./data.js";
const citySelectBoxRefKey = "city-select-box";
export default {
components: {
DxSelectBox,
DxForm,
DxSimpleItem,
DxLabel
},
data() {
const states = service.getStates(),
cities = service.getCities();
return {
states,
cities,
cityValue: null,
citySelectBoxRefKey,
address: service.getAddress(),
stateEditorOptions: {
dataSource: states,
valueExpr: "ID",
displayExpr: "Name"
},
cityEditorOptions: {
dataSource: cities,
valueExpr: "ID",
displayExpr: "Name"
}
};
},
methods: {
stateValueChanged: function(e) {
this.cityDataSource.filter(["StateID", "=", e.value]);
this.cityDataSource.load();
this.cityValue = null;
},
fieldDataChanged: function(e) {
if (e.dataField === "StateID") {
var cityEditor = e.component.getEditor("CityID");
let dataSource = cityEditor.getDataSource();
dataSource.filter(["StateID", "=", e.value]);
dataSource.load();
this.address.CityID = null;
}
}
},
computed: {
citySelectBox: function() {
return this.$refs[citySelectBoxRefKey].instance;
},
cityDataSource: function() {
return this.citySelectBox.getDataSource();
}
}
};
</script>
JavaScriptimport React, { useRef, useState, useCallback, useEffect } from "react";
import { SelectBox } from "devextreme-react/select-box";
import Form, { SimpleItem, Label } from "devextreme-react/form";
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.light.css';
import service from "./data.js";
const states = service.getStates();
const cities = service.getCities();
const stateEditorOptions = {
dataSource: states,
valueExpr: "ID",
displayExpr: "Name"
};
const cityEditorOptions = {
dataSource: cities,
valueExpr: "ID",
displayExpr: "Name"
};
export default function App() {
const [formData, setFormData] = useState(service.getAddress);
const [stateValue, setStateValue] = useState(null);
const [cityValue, setCityValue] = useState(null);
const citySelectBoxRef = useRef();
let cityDataSource = null;
useEffect(() => {
cityDataSource = citySelectBoxRef.current.instance.getDataSource();
}, [])
const stateValueChanged = (e) => {
cityDataSource.filter(["StateID", "=", e.value]);
cityDataSource.load();
setStateValue(e.value);
setCityValue(null)
}
const cityValueChanged = e => {
setCityValue(e.value);
};
const onFieldDataChanged = useCallback((e) => {
if (e.dataField === "StateID") {
let cityEditor = e.component.getEditor("CityID");
let dataSource = cityEditor.getDataSource();
dataSource.filter(["StateID", "=", e.value]);
dataSource.load();
setFormData({ ...formData, ...{ CityID: null, StateID: e.value } });
}
}, [])
return (
<div>
<div className="dx-fieldset" style={{width:"50%"}}>
<div className="dx-fieldset-header">Select State, City</div>
<div className="dx-field">
<div className="dx-field-label">State</div>
<div className="dx-field-value">
<SelectBox
dataSource={states}
valueExpr="ID"
displayExpr="Name"
value={stateValue}
onValueChanged={stateValueChanged}
/>
</div>
</div>
<div className="dx-field">
<div className="dx-field-label">City</div>
<div className="dx-field-value">
<SelectBox
ref={citySelectBoxRef}
dataSource={cities}
onValueChanged={cityValueChanged}
valueExpr="ID"
displayExpr="Name"
value={cityValue}
/>
</div>
</div>
<div className="dx-fieldset-header">In Form</div>
<div className="dx-field">
<Form
formData={formData}
onFieldDataChanged={onFieldDataChanged}
>
<SimpleItem
dataField="StateID"
editorType="dxSelectBox"
editorOptions={stateEditorOptions}
>
<Label text="State" />
</SimpleItem>
<SimpleItem
dataField="CityID"
editorType="dxSelectBox"
editorOptions={cityEditorOptions}
>
<Label text="City" />
</SimpleItem>
</Form>
</div>
</div>
</div>
);
}
Razor@using CascadingSelectBoxesSample.Models
<script>
function onValueChanged(e) {
let citySelectBox = $("#citySelectBox").dxSelectBox("instance");
let dataSource = citySelectBox.getDataSource();
dataSource.filter("StateID", "=", e.value);
dataSource.load();
citySelectBox.option("value", null);
}
function onFieldDataChanged(e) {
if (e.dataField === "StateID") {
let cityEditor = e.component.getEditor("CityID");
let dataSource = cityEditor.getDataSource();
dataSource.filter(["StateID", "=", e.value]);
dataSource.load();
e.component.updateData("CityID", null);
}
}
</script>
<div class="dx-viewport demo-container">
<div class="dx-fieldset" style="width:50%">
<div class="dx-fieldset-header">Standalone editors</div>
<div class="dx-field">
<div class="dx-field-label">State</div>
<div class="dx-field-value">
@(Html.DevExtreme().SelectBox()
.DisplayExpr("Name")
.ValueExpr("ID")
.DataSource(State.States, "ID")
.ShowClearButton(true)
.OnValueChanged("onValueChanged"))
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">City</div>
<div class="dx-field-value">
@(Html.DevExtreme().SelectBox().ID("citySelectBox")
.DisplayExpr("Name")
.ValueExpr("ID")
.DataSource(City.Cities, "ID"))
</div>
</div>
<div class="dx-fieldset-header">In Form</div>
<div class="dx-field">
@(Html.DevExtreme().Form<Address>()
.FormData(Address.Empty)
.OnFieldDataChanged("onFieldDataChanged")
.Items(items => {
// State item
items.AddSimpleFor(i => i.StateID)
.Label(l => l.Text("State"))
.Editor(e =>
e.SelectBox()
.DataSource(State.States, "ID")
.DisplayExpr("Name")
.ValueExpr("ID")
.ShowClearButton(true)
);
// City item
items.AddSimpleFor(i => i.CityID)
.Label(l => l.Text("City"))
.Editor(e =>
e.SelectBox()
.DataSource(City.Cities, "ID")
.DisplayExpr("Name")
.ValueExpr("ID")
);
})
)
</div>
</div>
</div>