$(() => {
$('#gridContainer').dxDataGrid({
dataSource: DevExpress.data.AspNet.createStore({
key: 'ID',
loadUrl: `${url}/Tasks`,
updateUrl: `${url}/UpdateTask`,
insertUrl: `${url}/InsertTask`,
onBeforeSend(method, ajaxOptions) {
ajaxOptions.xhrFields = { withCredentials: true };
},
}),
showBorders: true,
paging: {
enabled: true,
pageSize: 15,
},
headerFilter: {
visible: true,
},
searchPanel: {
visible: true,
},
editing: {
mode: 'cell',
allowUpdating: true,
allowAdding: true,
},
onRowInserted(e) {
e.component.navigateToRow(e.key);
},
columns: [{
dataField: 'Owner',
width: 150,
allowSorting: false,
lookup: {
dataSource: employees,
valueExpr: 'ID',
displayExpr: 'FullName',
},
validationRules: [{ type: 'required' }],
editCellTemplate: dropDownBoxEditorTemplate,
}, {
dataField: 'AssignedEmployee',
caption: 'Assignees',
width: 200,
allowSorting: false,
editCellTemplate: tagBoxEditorTemplate,
lookup: {
dataSource: employees,
valueExpr: 'ID',
displayExpr: 'FullName',
},
validationRules: [{ type: 'required' }],
cellTemplate(container, options) {
const noBreakSpace = '\u00A0';
const text = (options.value || []).map((element) => options.column.lookup.calculateCellValue(element)).join(', ');
container.text(text || noBreakSpace).attr('title', text);
},
calculateFilterExpression(filterValue, selectedFilterOperation, target) {
if (target === 'search' && typeof (filterValue) === 'string') {
return [this.dataField, 'contains', filterValue];
}
return function (data) {
return (data.AssignedEmployee || []).indexOf(filterValue) !== -1;
};
},
}, {
dataField: 'Subject',
validationRules: [{ type: 'required' }],
}, {
dataField: 'Status',
lookup: {
dataSource: statuses,
valueExpr: 'id',
displayExpr: 'name',
},
validationRules: [{ type: 'required' }],
width: 200,
editorOptions: {
itemTemplate(itemData, itemIndex, itemElement) {
if (itemData != null) {
const imageContainer = $('<span>').addClass('status-icon middle').appendTo(itemElement);
$('<img>').attr('src', `images/icons/status-${itemData.id}.svg`).appendTo(imageContainer);
$('<span>').addClass('middle').text(itemData.name).appendTo(itemElement);
} else {
$('<span>').text('(All)').appendTo(itemElement);
}
},
},
},
],
});
function dropDownBoxEditorTemplate(cellElement, cellInfo) {
return $('<div>').dxDropDownBox({
dropDownOptions: { width: 500 },
dataSource: employees,
value: cellInfo.value,
valueExpr: 'ID',
displayExpr: 'FullName',
inputAttr: { 'aria-label': 'Owner' },
contentTemplate(e) {
return $('<div>').dxDataGrid({
dataSource: employees,
remoteOperations: true,
columns: ['FullName', 'Title', 'Department'],
hoverStateEnabled: true,
scrolling: { mode: 'virtual' },
height: 250,
selection: { mode: 'single' },
selectedRowKeys: [cellInfo.value],
focusedRowEnabled: true,
focusedRowKey: cellInfo.value,
onSelectionChanged(selectionChangedArgs) {
e.component.option('value', selectionChangedArgs.selectedRowKeys[0]);
cellInfo.setValue(selectionChangedArgs.selectedRowKeys[0]);
if (selectionChangedArgs.selectedRowKeys.length > 0) {
e.component.close();
}
},
});
},
});
}
function tagBoxEditorTemplate(cellElement, cellInfo) {
return $('<div>').dxTagBox({
dataSource: employees,
value: cellInfo.value,
valueExpr: 'ID',
displayExpr: 'FullName',
inputAttr: { 'aria-label': 'Name' },
showSelectionControls: true,
maxDisplayedTags: 3,
showMultiTagOnly: false,
applyValueMode: 'useButtons',
searchEnabled: true,
onValueChanged(e) {
cellInfo.setValue(e.value);
},
onSelectionChanged() {
cellInfo.component.updateDimensions();
},
});
}
});
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DevExtreme Demo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>window.jQuery || document.write(decodeURIComponent('%3Cscript src="js/jquery.min.js"%3E%3C/script%3E'))</script>
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.1.5/css/dx.light.css" />
<script src="https://cdn3.devexpress.com/jslib/23.1.5/js/dx.all.js"></script>
<script src="https://unpkg.com/devextreme-aspnet-data@2.9.2/js/dx.aspnet.data.js"></script>
<script src="data.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css" />
<script src="index.js"></script>
</head>
<body class="dx-viewport">
<div class="demo-container">
<div id="gridContainer"></div>
</div>
</body>
</html>
.status-icon {
height: 16px;
width: 16px;
display: inline-block;
margin-right: 8px;
}
.middle {
vertical-align: middle;
}
const statuses = [{
id: 1, name: 'Not Started',
}, {
id: 2, name: 'In Progress',
}, {
id: 3, name: 'Deferred',
}, {
id: 4, name: 'Need Assistance',
}, {
id: 5, name: 'Completed',
},
];
const url = 'https://js.devexpress.com/Demos/Mvc/api/CustomEditors';
const employees = DevExpress.data.AspNet.createStore({
key: 'ID',
loadUrl: `${url}/Employees`,
onBeforeSend(method, ajaxOptions) {
ajaxOptions.xhrFields = { withCredentials: true };
},
});