Example T519278
Visible to All Users

DropDownBox for DevExtreme - How to filter data of a nested DataGrid

This example demonstrates how to filter the data of a DropDownBox embedded in a DataGrid component.

DropDownBox filtering

Use onInput, onOpened and onClosed event handlers to filter and display data.

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

Angular/src/app/app.component.html
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>
Angular/src/app/app.component.ts
TypeScript
import { 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]; } } }
jQuery/src/index.js
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; };
React/src/App.js
JavaScript
import 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;
Vue/src/App.vue
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>

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.