If you have technical questions, please create a support ticket in the DevExpress Support Center.
import React from 'react';
import * as AspNetData from 'devextreme-aspnet-data-nojquery';
import Grid from './Grid.tsx';
const url = 'https://js.devexpress.com/Demos/Mvc/api/DnDBetweenGrids';
const tasksStore = AspNetData.createStore({
key: 'ID',
loadUrl: `${url}/Tasks`,
updateUrl: `${url}/UpdateTask`,
onBeforeSend(method, ajaxOptions) {
ajaxOptions.xhrFields = { withCredentials: true };
},
});
const App = () => (
<div className="tables">
<div className="column">
<Grid tasksStore={tasksStore} status={1} />
</div>
<div className="column">
<Grid tasksStore={tasksStore} status={2} />
</div>
</div>
);
export default App;
xxxxxxxxxx
import React from 'react';
import * as AspNetData from 'devextreme-aspnet-data-nojquery';
import Grid from './Grid.js';
const url = 'https://js.devexpress.com/Demos/Mvc/api/DnDBetweenGrids';
const tasksStore = AspNetData.createStore({
key: 'ID',
loadUrl: `${url}/Tasks`,
updateUrl: `${url}/UpdateTask`,
onBeforeSend(method, ajaxOptions) {
ajaxOptions.xhrFields = { withCredentials: true };
},
});
const App = () => (
<div className="tables">
<div className="column">
<Grid
tasksStore={tasksStore}
status={1}
/>
</div>
<div className="column">
<Grid
tasksStore={tasksStore}
status={2}
/>
</div>
</div>
);
export default App;
xxxxxxxxxx
import React, { useCallback, useState } from 'react';
import DataGrid, {
Column, RowDragging, Scrolling, Lookup,
} from 'devextreme-react/data-grid';
const priorities = [{
id: 1, text: 'Low',
}, {
id: 2, text: 'Normal',
}, {
id: 3, text: 'High',
}, {
id: 4, text: 'Urgent',
}];
const Grid = ({ tasksStore, status }) => {
const [filterExpr] = useState(['Status', '=', status]);
const [dataSource] = useState({
store: tasksStore,
reshapeOnPush: true,
});
const onAdd = useCallback((e) => {
const key = e.itemData.ID;
const values = { Status: e.toData };
tasksStore.update(key, values).then(() => {
tasksStore.push([{
type: 'update', key, data: values,
}]);
});
}, [tasksStore]);
return (
<DataGrid
dataSource={dataSource}
height={440}
showBorders={true}
filterValue={filterExpr}
>
<RowDragging
data={status}
group="tasksGroup"
onAdd={onAdd}
/>
<Scrolling mode="virtual" />
<Column
dataField="Subject"
dataType="string"
/>
<Column
dataField="Priority"
dataType="number"
width={80}
>
<Lookup
dataSource={priorities}
valueExpr="id"
displayExpr="text"
/>
</Column>
<Column
dataField="Status"
dataType="number"
visible={false}
/>
</DataGrid>
);
};
export default Grid;
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.tsx';
ReactDOM.render(
<App />,
document.getElementById('app'),
);
xxxxxxxxxx
window.exports = window.exports || {};
window.config = {
transpiler: 'ts',
typescriptOptions: {
module: 'system',
emitDecoratorMetadata: true,
experimentalDecorators: true,
jsx: 'react',
},
meta: {
'react': {
'esModule': true,
},
'typescript': {
'exports': 'ts',
},
'devextreme/time_zone_utils.js': {
'esModule': true,
},
'devextreme/localization.js': {
'esModule': true,
},
'devextreme/viz/palette.js': {
'esModule': true,
},
'devextreme-aspnet-data-nojquery': {
'esModule': true,
},
'openai': {
'esModule': true,
},
},
paths: {
'npm:': 'https://unpkg.com/',
'bundles:': 'bundles/',
'externals:': 'bundles/externals/',
},
defaultExtension: 'js',
map: {
'ts': 'npm:plugin-typescript@8.0.0/lib/plugin.js',
'typescript': 'npm:typescript@4.2.4/lib/typescript.js',
'jszip': 'npm:jszip@3.10.1/dist/jszip.min.js',
'react': 'npm:react@17.0.2/umd/react.development.js',
'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js',
'prop-types': 'npm:prop-types/prop-types.js',
'devextreme-aspnet-data-nojquery': 'npm:devextreme-aspnet-data-nojquery@3.0.0/index.js',
'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js',
'luxon': 'npm:luxon@3.4.4/build/global/luxon.min.js',
'es6-object-assign': 'npm:es6-object-assign',
'devextreme': 'npm:devextreme@24.2.5/cjs',
'devextreme-react': 'npm:devextreme-react@24.2.5/cjs',
'devextreme-quill': 'npm:devextreme-quill@1.7.1/dist/dx-quill.min.js',
'devexpress-diagram': 'npm:devexpress-diagram@2.2.5/dist/dx-diagram.js',
'devexpress-gantt': 'npm:devexpress-gantt@4.1.54/dist/dx-gantt.js',
'@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12',
'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js',
'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js',
'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js',
'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js',
'inferno-hydrate': 'npm:inferno-hydrate/dist/inferno-hydrate.min.js',
'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js',
'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js',
'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js',
'devextreme-cldr-data': 'npm:devextreme-cldr-data@1.0.3',
// SystemJS plugins
'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js',
'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js',
// Prettier
'prettier/standalone': 'npm:prettier@2.8.8/standalone.js',
'prettier/parser-html': 'npm:prettier@2.8.8/parser-html.js',
},
packages: {
'devextreme': {
defaultExtension: 'js',
},
'devextreme-react': {
main: 'index.js',
},
'devextreme/events/utils': {
main: 'index',
},
'devextreme/localization/messages': {
format: 'json',
defaultExtension: 'json',
},
'devextreme/events': {
main: 'index',
},
'es6-object-assign': {
main: './index.js',
defaultExtension: 'js',
},
},
packageConfigPaths: [
'npm:@devextreme/*/package.json',
'npm:@devextreme/runtime@3.0.12/inferno/package.json',
],
babelOptions: {
sourceMaps: false,
stage0: true,
react: true,
},
};
System.config(window.config);
xxxxxxxxxx
import React, { useCallback, useState } from 'react';
import DataGrid, {
Column, RowDragging, Scrolling, Lookup,
} from 'devextreme-react/data-grid';
const priorities = [
{
id: 1,
text: 'Low',
},
{
id: 2,
text: 'Normal',
},
{
id: 3,
text: 'High',
},
{
id: 4,
text: 'Urgent',
},
];
const Grid = ({ tasksStore, status }) => {
const [filterExpr] = useState(['Status', '=', status]);
const [dataSource] = useState({
store: tasksStore,
reshapeOnPush: true,
});
const onAdd = useCallback(
(e) => {
const key = e.itemData.ID;
const values = { Status: e.toData };
tasksStore.update(key, values).then(() => {
tasksStore.push([
{
type: 'update',
key,
data: values,
},
]);
});
},
[tasksStore],
);
return (
<DataGrid
dataSource={dataSource}
height={440}
showBorders={true}
filterValue={filterExpr}
>
<RowDragging
data={status}
group="tasksGroup"
onAdd={onAdd}
/>
<Scrolling mode="virtual" />
<Column
dataField="Subject"
dataType="string"
/>
<Column
dataField="Priority"
dataType="number"
width={80}
>
<Lookup
dataSource={priorities}
valueExpr="id"
displayExpr="text"
/>
</Column>
<Column
dataField="Status"
dataType="number"
visible={false}
/>
</DataGrid>
);
};
export default Grid;
xxxxxxxxxx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(<App />, document.getElementById('app'));
xxxxxxxxxx
<html lang="en">
<head></head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"></div>
</div>
</body>
</html>
xxxxxxxxxx
.tables {
display: flex;
}
.column:first-child {
width: 50%;
padding-right: 15px;
}
.column:last-child {
width: 50%;
padding-left: 15px;
}
To allow users to move rows between grids, follow these steps:
-
Bind the grids to the same store
The store should be able to update data. In this demo, the store is created using the createStore method (part of the DevExtreme.AspNet.Data extension). The specifiedupdateUrl
enables the store to update data. -
Specify grid identifiers
Save them in the rowDragging.data property. The grids below have identifiers 1 and 2. -
Filter the grids to display different record sets
Use the identifiers in the filterValue property to filter the grids. The grids below display only the records whoseStatus
field equals the grid's identifier. -
Join the grids into one drag and drop group
Set the rowDragging.group property to the same value for all grids to allow moving rows between them. -
Update the data field that specifies where the row belongs
Implement the rowDragging.onAdd function. To access the target grid's identifier, use thetoData
function parameter. Call the store's update method to send this identifier to the server and push the same changes to the store on the client. The grids are refreshed automatically if you enable reshapeOnPush in the dataSource.