This example demonstrates how to filter the data of a DropDownBox embedded in a DataGrid component.
Use onInput, onOpened and onClosed event handlers to filter and display data.
Files to Review
- Angular
- jQuery
- React
- Vue
- 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<div class="dx-viewport demo-container">
<div class="dx-fieldset">
<div class="dx-field">
<div class="dx-field-label">DropDownBox with search and embedded DataGrid</div>
<div class="dx-field-value">
<dx-drop-down-box #dropDownBox
[dataSource]="dropDownBoxDataSource"
[(value)]="gridBoxValue"
(onValueChanged)="dropDownBoxValueChanged($event)"
valueExpr="OrderNumber"
[openOnFieldClick]="false"
[displayExpr]="gridBox_displayExpr"
[showClearButton]="true"
placeholder="Select a value..."
[acceptCustomValue]="true"
[(opened)]="gridBoxOpened"
valueChangeEvent=""
(onKeyDown)="onKeyDown($event)"
(onInput)="onInput($event)"
(onOpened)="onOpened($event)"
(onClosed)="onClosed($event)">
<dxo-drop-down-options [height]="300">
</dxo-drop-down-options>
<div *dxTemplate="let data of 'content'">
<dx-data-grid height="100%"
width="100%"
(onInitialized)="dataGridOnInitialized($event)"
[columnWidth]="100"
[(selectedRowKeys)]="gridBoxValue"
[focusedRowIndex]="focusedRowIndex"
[dataSource]="dataSource"
[remoteOperations]="true"
[focusedRowEnabled]="true"
[hoverStateEnabled]="true"
[(focusedRowKey)]="focusedRowKey"
[autoNavigateToFocusedRow]="false"
(onKeyDown)="dataGridOnKeyDown($event)"
(onContentReady)="dataGridOnContentReady($event)">
<dxi-column dataField="OrderNumber" caption="ID" dataType="number"></dxi-column>
<dxi-column dataField="OrderDate" dataType="date" format="shortDate"></dxi-column>
<dxi-column dataField="StoreState" dataType="string"></dxi-column>
<dxi-column dataField="StoreCity" dataType="string"></dxi-column>
<dxi-column dataField="Employee" dataType="string"></dxi-column>
<dxi-column dataField="SaleAmount" dataType="number">
<dxo-format type="currency" [precision]="2"></dxo-format>
</dxi-column>
<dxo-pager [visible]="false"></dxo-pager>
<dxo-paging [enabled]="true" [pageSize]="10"></dxo-paging>
<dxo-selection mode="single"></dxo-selection>
<dxo-scrolling mode="virtual"></dxo-scrolling>
</dx-data-grid>
</div>
</dx-drop-down-box>
</div>
</div>
</div>
</div>
TypeScriptimport { Component, AfterViewInit, ViewChild } from '@angular/core';
import * as AspNetData from "devextreme-aspnet-data-nojquery";
import DataSource from 'devextreme/data/data_source';
import { DxDropDownBoxComponent } from 'devextreme-angular';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements AfterViewInit {
dataGrid: any;
dropDownBoxDataSource: DataSource;
searchTimer: any;
ddbInstance: any;
dataSource: any;
gridBoxValue: any;
gridBoxOpened: boolean;
focusedRowIndex: number;
focusedRowKey: number;
@ViewChild("dropDownBox", { static: false }) dropDownBox: DxDropDownBoxComponent;
constructor() {
this.focusedRowIndex = 0;
this.gridBoxValue = [35711];
this.dataSource = new DataSource({
store: this.makeAsyncDataSource(),
searchExpr: ["StoreCity", "StoreState", "Employee"]
});
this.dropDownBoxDataSource = new DataSource({
store: this.makeAsyncDataSource()
});
this.gridBoxOpened = false;
}
ngAfterViewInit(): void {
this.ddbInstance = this.dropDownBox.instance;
}
makeAsyncDataSource() {
return AspNetData.createStore({
key: "OrderNumber",
loadUrl: "https://js.devexpress.com/Demos/WidgetsGalleryDataService/api/orders"
});
};
gridBox_displayExpr(item) {
return (
item &&
`${item.Employee}: ${item.StoreState} - ${item.StoreCity} <${item.OrderNumber}>`
);
}
dropDownBoxValueChanged(args) {
clearTimeout(this.searchTimer);
this.gridBoxOpened = false;
}
isSearchIncomplete(dropDownBox) {
// compare the last displayed value and the current real text in the input field
let displayValue = dropDownBox.option("displayValue"),
text = dropDownBox.option("text");
text = text && text.length && text;
displayValue = displayValue && displayValue.length && displayValue[0];
return text !== displayValue;
};
onInput(e: any) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
let text = e.component.option("text"),
opened = this.gridBoxOpened;
this.dataSource.searchValue(text);
if (opened && this.isSearchIncomplete(e.component)) {
this.dataSource.load().done((items) => {
if (items.length > 0 && this.dataGrid)
this.focusedRowKey = items[0].OrderNumber;
});
} else {
this.gridBoxOpened = true;
}
}, 500);
}
onOpened(e: any) {
let ddbInstance = e.component;
if (ddbInstance.isKeyDown) {
var contentReadyHandler = args => {
let gridInstance = args.component;
gridInstance.focus();
gridInstance.off("contentReady", contentReadyHandler);
};
if (!this.dataGrid.isNotFirstLoad)
this.dataGrid.on("contentReady", contentReadyHandler);
else {
var optionChangedHandler = (args) => {
let gridInstance = args.component;
if (args.name === 'focusedRowKey' || args.name === 'focusedColumnIndex') {
gridInstance.off('optionChanged', optionChangedHandler);
gridInstance.focus();
}
}
this.dataGrid.on('optionChanged', optionChangedHandler);
this.focusedRowIndex = 0;
}
ddbInstance.isKeyDown = false;
} else if (this.dataGrid.isNotFirstLoad && this.isSearchIncomplete(ddbInstance)) {
this.dataSource.load().done(items => {
if (items.length > 0)
this.focusedRowKey = items[0].OrderNumber;
ddbInstance.focus();
});
}
}
onClosed(e: any) {
let ddbInstance = e.component,
searchValue = this.dataSource.searchValue();
if (this.isSearchIncomplete(ddbInstance)) {
this.gridBoxValue = this.gridBoxValue === "" ? null : "";
}
if (searchValue) {
this.dataSource.searchValue(null);
}
}
onKeyDown(e: any) {
let ddbInstance = e.component;
if (e.event.keyCode !== 40) return; //not arrow down
if (!this.gridBoxOpened) {
ddbInstance.isKeyDown = true;
this.gridBoxOpened = true;
} else this.dataGrid && this.dataGrid.focus();
}
dataGridOnInitialized(e: any) {
this.dataGrid = e.component;
}
dataGridOnContentReady = (e: any) => {
if (!e.component.isNotFirstLoad) {
e.component.isNotFirstLoad = true;
this.ddbInstance.focus();
}
}
dataGridOnKeyDown(e: any) {
if (e.event.keyCode === 13) {// Enter press
this.focusedRowIndex = e.component.option('focusedRowIndex')
this.gridBoxValue = [this.focusedRowKey];
}
}
}
JavaScript$(function () {
let dataGrid,
searchTimer,
dataSource = new DevExpress.data.DataSource({
store: makeAsyncDataSource(),
searchExpr: ["StoreCity", "StoreState", "Employee"]
});
$("#gridBox").dxDropDownBox({
value: 35711,
valueExpr: "OrderNumber",
displayExpr: function (item) {
return (
item &&
`${item.Employee}: ${item.StoreState} - ${item.StoreCity} <${item.OrderNumber}>`
);
},
acceptCustomValue: true,
openOnFieldClick: false,
valueChangeEvent: "",
showClearButton: true,
dataSource: makeAsyncDataSource(),
placeholder: "Select a value...",
dropDownOptions: {
height: 300
},
onInput: function (e) {
clearTimeout(searchTimer);
searchTimer = setTimeout(function () {
let text = e.component.option("text"),
opened = e.component.option("opened");
dataSource.searchValue(text);
if (opened && isSearchIncomplete(e.component)) {
dataSource.load().done((items) => {
if (items.length > 0 && dataGrid)
dataGrid.option("focusedRowKey", items[0].OrderNumber)
});
} else {
e.component.open();
}
}, 500);
},
onOpened: function (e) {
let ddbInstance = e.component;
if (ddbInstance.isKeyDown) {
var contentReadyHandler = args => {
let gridInstance = args.component;
gridInstance.focus();
gridInstance.off("contentReady", contentReadyHandler);
};
if (!dataGrid.isNotFirstLoad)
dataGrid.on("contentReady", contentReadyHandler);
else {
var optionChangedHandler = (args) => {
let gridInstance = args.component;
if (args.name === 'focusedRowKey' || args.name === 'focusedColumnIndex') {
gridInstance.off('optionChanged', optionChangedHandler);
gridInstance.focus();
}
}
dataGrid.on('optionChanged', optionChangedHandler);
dataGrid.option("focusedRowIndex", 0)
}
ddbInstance.isKeyDown = false;
} else if (dataGrid.isNotFirstLoad && isSearchIncomplete(ddbInstance)) {
dataSource.load().done(items => {
if (r.length > 0)
dataGrid.option("focusedRowKey", items[0].OrderNumber)
ddbInstance.focus();
});
}
},
onClosed: function (e) {
let ddbInstance = e.component,
value = ddbInstance.option("value"),
searchValue = dataSource.searchValue();
if (isSearchIncomplete(ddbInstance)) {
ddbInstance.option("value", value === "" ? null : "");
}
if (searchValue) {
dataSource.searchValue(null);
}
},
onKeyDown: function (e) {
let ddbInstance = e.component;
if (e.event.keyCode !== 40) return; //not arrow down
if (!ddbInstance.option("opened")) {
ddbInstance.isKeyDown = true;
ddbInstance.open();
} else dataGrid && dataGrid.focus();
},
contentTemplate: function (e, container) {
let value = e.component.option("value"),
ddbInstance = e.component;
$dataGridContainer = $("<div>");
container.append($dataGridContainer);
$dataGridContainer.dxDataGrid({
dataSource: dataSource,
hoverStateEnabled: true,
paging: { enabled: true, pageSize: 10 },
focusedRowIndex: 0,
focusedRowEnabled: true,
autoNavigateToFocusedRow: false,
onContentReady: function (e) {
if (!e.component.isNotFirstLoad) {
e.component.isNotFirstLoad = true;
ddbInstance.focus();
}
},
remoteOperations: true,
scrolling: { mode: "virtual" },
selection: { mode: "single" },
selectedRowKeys: [value],
height: "100%",
width: '100%',
columnWidth: 100,
onKeyDown: function (e) {
let gridInstance = e.component;
if (e.event.keyCode === 13) // Enter press
gridInstance.selectRows(
[gridInstance.option("focusedRowKey")],
false
);
},
onSelectionChanged: function (e) {
let keys = e.selectedRowKeys,
hasSelection = keys.length;
ddbInstance.option("value", hasSelection ? keys[0] : null);
},
columns: [
{
dataField: "OrderNumber",
caption: "ID",
dataType: "number"
},
{
dataField: "OrderDate",
dataType: "date",
format: "shortDate"
},
{
dataField: "StoreCity",
dataType: "string"
},
{
dataField: "StoreState",
dataType: "string"
},
{
dataField: "Employee",
dataType: "string"
},
{
dataField: "SaleAmount",
dataType: "number",
format: {
type: "currency",
precision: 2
}
}
]
});
dataGrid = $dataGridContainer.dxDataGrid("instance");
ddbInstance.on("valueChanged", function (args) {
clearTimeout(searchTimer);
dataGrid.option("selectedRowKeys", args.value ? [args.value] : []);
ddbInstance.close();
});
return container;
}
});
});
function makeAsyncDataSource() {
return DevExpress.data.AspNet.createStore({
key: "OrderNumber",
loadUrl: "https://js.devexpress.com/Demos/WidgetsGalleryDataService/api/orders"
});
};
function isSearchIncomplete(dropDownBox) {
// compare the last displayed value and the current real text in the input field
let displayValue = dropDownBox.option("displayValue"),
text = dropDownBox.option("text");
text = text && text.length && text; //text[0];
displayValue = displayValue && displayValue.length && displayValue[0];
return text !== displayValue;
};
JavaScriptimport React, { useMemo, useEffect, useReducer, useContext } from 'react';
import 'devextreme/dist/css/dx.common.css';
import 'devextreme/dist/css/dx.material.blue.light.compact.css';
import './App.css';
import DropDownBox, { DropDownOptions } from 'devextreme-react/drop-down-box';
import DataGrid, { Column, Format, Selection, Paging, FilterRow, Scrolling } from 'devextreme-react/data-grid';
import * as AspNetData from "devextreme-aspnet-data-nojquery";
import DataSource from 'devextreme/data/data_source';
const makeAsyncDataSource = function () {
return AspNetData.createStore({
key: "OrderNumber",
loadUrl: "https://js.devexpress.com/Demos/WidgetsGalleryDataService/api/orders"
});
};
const gridBox_displayExpr = function (item) {
return (
item &&
`${item.Employee}: ${item.StoreState} - ${item.StoreCity} <${item.OrderNumber}>`
);
}
let searchTimer = null;
const initialState = {
value: [35711],
focusedRowIndex: 0,
focusedRowKey: null,
opened: false,
dataGridInstance: null
}
function reducer(state, action) {
switch (action.type) {
case 'all':
return {
...state,
opened: action.opened,
value: action.value
}
case 'open/close':
return {
...state,
opened: action.opened
}
case "dataGridInstance":
return {
...state,
dataGridInstance: action.instance
}
case 'focusedRowKey':
return {
...state,
focusedRowIndex: action.focusedRowIndex,
focusedRowKey: action.focusedRowKey
}
case 'value': {
return {
...state,
value: action.value
}
}
default:
throw new Error("non-processed action: ", action.type);
}
}
function isSearchIncomplete(dropDownBox) {
// compare the last displayed value and the current real text in the input field
let displayValue = dropDownBox.option("displayValue"),
text = dropDownBox.option("text");
text = text && text.length && text;
displayValue = displayValue && displayValue.length && displayValue[0];
return text !== displayValue;
};
const DropDownBoxDispatch = React.createContext(null);
function App() {
const dataSource = useMemo(() => new DataSource({
store: makeAsyncDataSource()
}), []);
const gridDataSource = useMemo(() => new DataSource({
store: makeAsyncDataSource(),
searchExpr: ["StoreCity", "StoreState", "Employee"]
}), []);
const [state, dispatch] = useReducer(reducer, initialState);
let { value, opened, focusedRowIndex, focusedRowKey, dataGridInstance } = state;
const dropDownBoxValueChanged = (args) => {
clearTimeout(searchTimer);
dispatch({ value: args.value, opened: false, type: 'all' })
}
const onInput = function (e) {
clearTimeout(searchTimer);
searchTimer = setTimeout(function () {
let text = e.component.option("text");
gridDataSource.searchValue(text);
if (opened && isSearchIncomplete(e.component)) {
gridDataSource.load().then((items) => {
if (items.length > 0 && dataGridInstance)
dispatch({ focusedRowKey: items[0].OrderNumber, type: 'focusedRowKey' })
});
} else {
dispatch({ opened: true, type: 'open/close' })
}
}, 500);
}
const onKeyDown = (e) => {
let ddbInstance = e.component;
if (e.event.keyCode !== 40) return; //not arrow down
if (!opened) {
ddbInstance.isKeyDown = true;
dispatch({ opened: true, type: 'open/close' })
} else dataGridInstance && dataGridInstance.focus();
}
const onOpened = (e) => {
let ddbInstance = e.component;
if (ddbInstance.isKeyDown) {
if (!dataGridInstance)
return;
var contentReadyHandler = args => {
let gridInstance = args.component;
gridInstance.focus();
gridInstance.off("contentReady", contentReadyHandler);
};
if (!dataGridInstance.isNotFirstLoad)
dataGridInstance.on("contentReady", contentReadyHandler);
else {
var optionChangedHandler = (args) => {
let gridInstance = args.component;
if (args.name === 'focusedRowKey' || args.name === 'focusedColumnIndex') {
gridInstance.off('optionChanged', optionChangedHandler);
gridInstance.focus();
}
}
dataGridInstance.on('optionChanged', optionChangedHandler);
dispatch({ type: 'focusedRowKey', focusedRowKey: null, focusedRowIndex: 0 })
}
ddbInstance.isKeyDown = false;
} else if (dataGridInstance && dataGridInstance.isNotFirstLoad && isSearchIncomplete(ddbInstance)) {
gridDataSource.load().done(items => {
if (items.length > 0)
dispatch({ focusedRowKey: items[0].OrderNumber, type: 'focusedRowKey' })
ddbInstance.focus();
});
}
}
const onClosed = (e) => {
let ddbInstance = e.component,
searchValue = gridDataSource.searchValue();
if (isSearchIncomplete(ddbInstance)) {
dispatch({ value: value === "" ? null : "", type: 'value' })
}
if (searchValue) {
gridDataSource.searchValue(null);
}
}
const onOptionChanged = (args) => {
if (args.name === "opened") {
dispatch({ opened: args.value, type: 'open/close' })
}
}
useEffect(() => {
// A DataSource instance created outside a widget should be disposed of manually
return () => {
dataSource.dispose();
gridDataSource.dispose();
dispatch({ type: 'dataGridInstance', instance: null })
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return (
<div className="dx-viewport demo-container">
<div className="dx-fieldset">
<div className="dx-field">
<div className="dx-field-label">DropDownBox with search and embedded DataGrid</div>
<div className="dx-field-value">
<DropDownBoxDispatch.Provider value={{
dataSource: gridDataSource,
dispatch: dispatch,
focusedRowKey: focusedRowKey,
focusedRowIndex: focusedRowIndex
}}>
<DropDownBox showClearButton={true}
placeholder="Select a value..."
onInput={onInput}
displayExpr={gridBox_displayExpr}
valueExpr="OrderNumber"
value={value}
valueChangeEvent=""
acceptCustomValue={true}
onOpened={onOpened}
opened={opened}
openOnFieldClick={false}
dataSource={dataSource}
onKeyDown={onKeyDown}
onClosed={onClosed}
onValueChanged={dropDownBoxValueChanged}
onOptionChanged={onOptionChanged}
contentComponent={DataGridComponent}
>
<DropDownOptions height={300} />
</DropDownBox>
</DropDownBoxDispatch.Provider>
</div>
</div>
</div>
</div>
);
}
const DataGridComponent = ({ data }) => {
const { value, component } = data;
const { dispatch, dataSource, focusedRowKey, focusedRowIndex } = useContext(DropDownBoxDispatch);
const selectionChanged = (e) => {
dispatch({ value: e.selectedRowKeys, opened: false, type: 'all' })
}
const contentReady = (e) => {
if (!e.component.isNotFirstLoad) {
e.component.isNotFirstLoad = true;
component.focus();
dispatch({ instance: e.component, type: 'dataGridInstance' })
}
}
const keyDown = (e) => {
if (e.event.keyCode === 13) {// Enter press
dispatch({ value: [focusedRowKey], opened: false, type: 'all' });
}
}
const focusedRowChanged = (e) => {
dispatch({ focusedRowIndex: e.rowIndex, focusedRowKey: e.component.getKeyByRowIndex(e.rowIndex), type: 'focusedRowKey' })
}
return (
<DataGrid
onFocusedRowChanged={focusedRowChanged}
dataSource={dataSource}
focusedRowEnabled={true}
onContentReady={contentReady}
autoNavigateToFocusedRow={false}
remoteOperations={true}
hoverStateEnabled={true}
onKeyDown={keyDown}
focusedRowIndex={focusedRowIndex}
focusedRowKey={focusedRowKey}
onSelectionChanged={selectionChanged}
defaultSelectedRowKeys={value}
columnWidth={100}
width="100%"
height="100%"
>
<Column dataField="OrderNumber" caption="ID" dataType="number" />
<Column dataField="OrderDate" format="shortDate" dataType="date" />
<Column dataField="StoreState" dataType="string" />
<Column dataField="StoreCity" dataType="string" />
<Column dataField="Employee" dataType="string" />
<Column dataField="SaleAmount" dataType="number" >
<Format type="currency" precision={2} />
</Column>
<Selection mode="single" />
<Scrolling mode="virtual" />
<Paging enabled={true} pageSize={10} />
<FilterRow visible={true} />
</DataGrid>
)
}
export default App;
Code<template>
<div id="app">
<DropDownBoxWithDataGrid />
</div>
</template>
<script>
import DropDownBoxWithDataGrid from './components/DropDownBoxWithDataGrid.vue'
export default {
name: 'app',
components: {
DropDownBoxWithDataGrid
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>