Your search did not match any results.
Data Grid

Advanced Master-Detail View

This demo illustrates an advanced master-detail view in the DataGrid component. Master rows represent suppliers. Detail sections contain TabPanel components with two tabs: Orders and Address. In the Orders tab, you can choose a supplier's product from the SelectBox, and the DataGrid under it will show orders placed on this product. The Address tab displays the supplier's address.

Backend API
Copy to CodePen
Apply
Reset
const DemoApp = angular.module('DemoApp', ['dx']); const url = 'https://js.devexpress.com/Demos/Mvc/api/DataGridAdvancedMasterDetailView'; DemoApp.controller('DemoController', ($scope) => { $scope.gridOptions = { showBorders: true, dataSource: DevExpress.data.AspNet.createStore({ key: 'SupplierID', loadUrl: `${url}/GetSuppliers`, }), paging: { pageSize: 15, }, remoteOperations: true, columns: [ 'ContactName', 'ContactTitle', 'CompanyName', 'City', 'Country', ], masterDetail: { enabled: true, template: 'masterDetail', }, }; $scope.getTabPanelOptions = function () { return { items: [{ title: 'Orders', template: 'ordersTab', }, { title: 'Address', template: 'addressTab', }], }; }; $scope.getAddressTabOptions = function (data) { return { formData: data, colCount: 2, customizeItem(item) { item.template = 'formItemTemplate'; }, items: ['Address', 'City', 'Region', 'PostalCode', 'Country', 'Phone'], }; }; }); DemoApp.controller('OrdersTabController', ($scope) => { function createOrderHistoryStore(productID) { return DevExpress.data.AspNet.createStore({ key: 'OrderID', loadParams: { ProductID: productID }, loadUrl: `${url}/GetOrdersByProduct`, }); } $scope.getOrdersTabOptions = function () { return { labelLocation: 'top', items: [{ label: { text: 'Product' }, template: 'productSelectBox', }, { label: { text: 'Order History' }, template: 'orderHistory', }], }; }; $scope.getSelectBoxOptions = function (masterDetailData) { return { dataSource: DevExpress.data.AspNet.createStore({ key: 'ProductID', loadParams: { SupplierID: masterDetailData.SupplierID }, loadUrl: `${url}/GetProductsBySupplier`, }), valueExpr: 'ProductID', displayExpr: 'ProductName', deferRendering: false, onContentReady(e) { const firstItem = e.component.option('items[0]'); if (firstItem) { e.component.option('value', firstItem.ProductID); } }, onValueChanged(e) { $scope.dataSource = createOrderHistoryStore(e.value); }, }; }; $scope.orderHistoryDataGridOptions = { bindingOptions: { dataSource: { deep: false, dataPath: 'dataSource', }, }, paging: { pageSize: 5, }, showBorders: true, columns: [ 'OrderID', { dataField: 'OrderDate', dataType: 'date', }, 'ShipCountry', 'ShipCity', { dataField: 'UnitPrice', format: 'currency', }, 'Quantity', { dataField: 'Discount', format: 'percent', }, ], summary: { totalItems: [{ column: 'UnitPrice', summaryType: 'sum', valueFormat: { format: 'currency', precision: 2, }, }, { column: 'Quantity', summaryType: 'count', }], }, }; });
<!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/22.2.6/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script> <script>window.angular || document.write(decodeURIComponent('%3Cscript src="js/angular.min.js"%3E%3C/script%3E'))</script> <script src="https://cdn3.devexpress.com/jslib/22.2.6/js/dx.all.js"></script> <script src="https://unpkg.com/devextreme-aspnet-data@2.9.0/js/dx.aspnet.data.js"></script> <script src="index.js"></script> </head> <body class="dx-viewport"> <div class="demo-container" ng-app="DemoApp" ng-controller="DemoController"> <div dx-data-grid="gridOptions" dx-item-alias="masterDetailItem" id="gridContainer"> <div data-options="dxTemplate: {name: 'masterDetail'}"> <div dx-tab-panel="getTabPanelOptions(masterDetailItem.data)"> <div data-options="dxTemplate: {name: 'ordersTab'}"> <div ng-controller="OrdersTabController"> <div dx-form="getOrdersTabOptions(masterDetailItem.data)" class="form-container"> <div data-options="dxTemplate: {name: 'productSelectBox'}"> <div dx-select-box="getSelectBoxOptions(masterDetailItem.data)"></div> </div> <div data-options="dxTemplate: {name: 'orderHistory'}"> <div dx-data-grid="orderHistoryDataGridOptions"></div> </div> </div> </div> </div> <div data-options="dxTemplate: {name: 'addressTab'}"> <div dx-form="getAddressTabOptions(masterDetailItem.data)" dx-item-alias="formItem" class="address-form form-container"> <div data-options="dxTemplate: {name: 'formItemTemplate'}"> <span>{{formItem.editorOptions.value}}</span> </div> </div> </div> </div> </div> </div> </div> </body> </html>
#gridContainer { height: 620px; } .dx-datagrid-rowsview .dx-master-detail-row:not(.dx-datagrid-edit-form) > .dx-datagrid-group-space, .dx-datagrid-rowsview .dx-master-detail-row:not(.dx-datagrid-edit-form) .dx-master-detail-cell { background-color: transparent; } .form-container { padding: 20px; } .address-form label { font-weight: bold; }