Your search did not match any results.
Diagram

Templates with Editing

In this demo, the customShapeTemplate option defines a common shape template and adds the Edit and Delete links to a shape. These links allow users to modify and remove employee data in the data source. The Diagram widget reloads the modified diagram data every time the data source changes.

The onRequestLayoutUpdate function specifies whether the widget should reapply the auto layout after the diagram is reloaded.

The customDataExpr option links custom employee information from the data source to diagram nodes. Changes made to the data are reflected in the diagram history. The undo and redo actions in the UI allow users to revert these changes.

The CustomShapeToolboxTemplate option specifies a template for a shape in the toolbox.

Copy to CodeSandBox
Apply
Reset
<template> <div id="container"> <DxDiagram id="diagram" ref="diagram" custom-shape-template="CustomShapeTemplate" custom-shape-toolbox-template="CustomShapeToolboxTemplate" @request-layout-update="onRequestLayoutUpdate" > <DxCustomShape :type="'employee'" :category="'employee'" :base-type="'rectangle'" :title="'New Employee'" :default-width="1.5" :default-height="1" :toolbox-width-to-height-ratio="2" :min-width="1.5" :min-height="1" :max-width="3" :max-height="2" :allow-edit-text="false" /> <template #CustomShapeTemplate="{ data }"> <CustomShapeTemplate :employee="data.dataItem" :edit-employee="editEmployee" :delete-employee="deleteEmployee" /> </template> <template #CustomShapeToolboxTemplate="{ data }"> <CustomShapeToolboxTemplate/> </template> <DxNodes :data-source="dataSource" :key-expr="'ID'" :type-expr="itemTypeExpr" :custom-data-expr="itemCustomDataExpr" :parent-key-expr="'Head_ID'" > <DxAutoLayout :type="'tree'"/> </DxNodes> <DxContextToolbox :shape-icons-per-row="1" :width="100" /> <DxToolbox :show-search="false" :shape-icons-per-row="1" > <DxGroup :category="'employee'" :title="'Employee'" :expanded="true" /> </DxToolbox> <DxPropertiesPanel> <DxTab> <DxGroup :title="'Page Properties'" :commands="['pageSize', 'pageOrientation' , 'pageColor' ]" /> </DxTab> </DxPropertiesPanel> </DxDiagram> <DxPopup v-model:visible="popupVisible" :drag-enabled="false" :show-title="true" :width="400" :height="480" title="Edit Employee" > <div class="dx-fieldset"> <div class="dx-field"> <div class="dx-field-label">Name</div> <div class="dx-field-value"> <DxTextBox v-model:value="currentEmployee.Full_Name"/> </div> </div> <div class="dx-field"> <div class="dx-field-label">Title</div> <div class="dx-field-value"> <DxTextBox v-model:value="currentEmployee.Title"/> </div> </div> <div class="dx-field"> <div class="dx-field-label">City</div> <div class="dx-field-value"> <DxTextBox v-model:value="currentEmployee.City"/> </div> </div> <div class="dx-field"> <div class="dx-field-label">State</div> <div class="dx-field-value"> <DxTextBox v-model:value="currentEmployee.State"/> </div> </div> <div class="dx-field"> <div class="dx-field-label">Email</div> <div class="dx-field-value"> <DxTextBox v-model:value="currentEmployee.Email"/> </div> </div> <div class="dx-field"> <div class="dx-field-label">Skype</div> <div class="dx-field-value"> <DxTextBox v-model:value="currentEmployee.Skype"/> </div> </div> <div class="dx-field"> <div class="dx-field-label">Phone</div> <div class="dx-field-value"> <DxTextBox v-model:value="currentEmployee.Mobile_Phone"/> </div> </div> </div> <div class="dx-fieldset buttons"> <DxButton :text="'Update'" :type="'default'" @click="updateEmployee" /> <DxButton :text="'Cancel'" @click="cancelEditEmployee" /> </div> </DxPopup> </div> </template> <script> import { DxDiagram, DxNodes, DxAutoLayout, DxCustomShape, DxContextToolbox, DxPropertiesPanel, DxGroup, DxTab, DxToolbox } from 'devextreme-vue/diagram'; import { DxPopup } from 'devextreme-vue/popup'; import DxTextBox from 'devextreme-vue/text-box'; import DxButton from 'devextreme-vue/button'; import ArrayStore from 'devextreme/data/array_store'; import CustomShapeTemplate from './CustomShapeTemplate.vue'; import CustomShapeToolboxTemplate from './CustomShapeToolboxTemplate.vue'; import service from './data.js'; export default { components: { DxDiagram, DxNodes, DxAutoLayout, DxCustomShape, DxContextToolbox, DxPropertiesPanel, DxGroup, DxTab, DxToolbox, CustomShapeTemplate, CustomShapeToolboxTemplate, DxPopup, DxTextBox, DxButton }, data() { var that = this; this.generatedID = 100; return { employees: service.getEmployees(), dataSource: new ArrayStore({ key: 'ID', data: service.getEmployees(), onInserting: function(values) { values.ID = values.ID || that.generatedID++; values.Full_Name = values.Full_Name || "Employee's Name"; values.Title = values.Title || "Employee's Title"; } }), currentEmployee: {}, popupVisible: false }; }, methods: { itemTypeExpr() { return 'employee'; }, itemCustomDataExpr(obj, value) { if(value === undefined) { return { 'Full_Name': obj.Full_Name, 'Prefix': obj.Prefix, 'Title': obj.Title, 'City': obj.City, 'State': obj.State, 'Email': obj.Email, 'Skype': obj.Skype, 'Mobile_Phone': obj.Mobile_Phone }; } else { obj.Full_Name = value.Full_Name; obj.Prefix = value.Prefix; obj.Title = value.Title; obj.City = value.City; obj.State = value.State; obj.Email = value.Email; obj.Skype = value.Skype; obj.Mobile_Phone = value.Mobile_Phone; } }, onRequestLayoutUpdate(e) { for(var i = 0; i < e.changes.length; i++) { if(e.changes[i].type === 'remove') { e.allowed = true; } else if(e.changes[i].data.Head_ID !== undefined && e.changes[i].data.Head_ID !== null) { e.allowed = true; } } }, editEmployee(employee) { this.currentEmployee = Object.assign({ 'Full_Name': '', 'Prefix': '', 'Title': '', 'City': '', 'State': '', 'Email': '', 'Skype': '', 'Mobile_Phone': '' }, employee); this.popupVisible = true; }, deleteEmployee(employee) { this.dataSource.push([{ type: 'remove', key: employee.ID }]); }, updateEmployee() { this.dataSource.push([{ type: 'update', key: this.currentEmployee.ID, data: { 'Full_Name': this.currentEmployee.Full_Name, 'Title': this.currentEmployee.Title, 'City': this.currentEmployee.City, 'State': this.currentEmployee.State, 'Email': this.currentEmployee.Email, 'Skype': this.currentEmployee.Skype, 'Mobile_Phone': this.currentEmployee.Mobile_Phone } }]); this.currentEmployee = {}; this.popupVisible = false; }, cancelEditEmployee() { this.currentEmployee = {}; this.popupVisible = false; } } }; </script> <style scoped> #diagram { height: 725px; } .dx-popup-content { padding: 0; } .dx-popup-content .dx-fieldset.buttons { display: flex; justify-content: flex-end; } .dx-popup-content .dx-fieldset.buttons > * { margin-left: 8px; } </style>
<template> <svg class="template"> <text class="template-name" x="50%" y="20%" > {{ employeeName }} </text> <text class="template-title" x="50%" y="45%" > {{ employeeTitle }} </text> <text class="template-button" x="40%" y="85%" @click="editEmployeeFunc" > Edit </text> <text class="template-button" x="62%" y="85%" @click="deleteEmployeeFunc" > Delete </text> </svg> </template> <script> export default { props: { employee: { type: Object, required: true, default: () => ({}) }, editEmployee: { type: Function, required: true, default: () => {} }, deleteEmployee: { type: Function, required: true, default: () => {} } }, computed: { employeeName() { return this.employee && this.employee.Full_Name ? this.employee.Full_Name : 'Employee\'s Name'; }, employeeTitle() { return this.employee && this.employee.Title ? this.employee.Title : 'Employee\'s Title'; } }, methods: { editEmployeeFunc() { this.editEmployee(this.employee); }, deleteEmployeeFunc() { this.deleteEmployee(this.employee); } } }; </script> <style> #diagram .template .template-name { font-weight: bold; text-decoration: underline; } #diagram .template .template-title { font-style: italic; } #diagram .template .template-button { cursor: pointer; font-size: 8pt; fill: navy; } #diagram .template .template-button:hover { text-decoration: underline; } </style>
<template> <svg class="template"> <text x="50%" y="40%" > New </text> <text x="50%" y="70%" > Employee </text> </svg> </template> <script> export default { props: { }, methods: { } }; </script> <style> #diagram .template .template-name { font-weight: bold; text-decoration: underline; } #diagram .template .template-title { font-style: italic; } </style>
import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app');
<!DOCTYPE html> <html> <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" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/20.2.3/css/dx.common.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/20.2.3/css/dx.light.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/20.2.3/css/dx-diagram.css" /> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" /> <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script> <script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script> <script type="text/javascript" src="config.js"></script> <script type="text/javascript"> System.import('./index.js'); </script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="app"> </div> </div> </body> </html>
var employees = [{ 'ID': 1, 'Head_ID': 0, 'Full_Name': 'John Heart', 'Prefix': 'Mr.', 'Title': 'CEO', 'City': 'Los Angeles', 'State': 'California', 'Email': 'jheart@dx-email.com', 'Skype': 'jheart_DX_skype', 'Mobile_Phone': '(213) 555-9392', 'Birth_Date': '1964-03-16', 'Hire_Date': '1995-01-15' }, { 'ID': 2, 'Head_ID': 1, 'Full_Name': 'Samantha Bright', 'Prefix': 'Dr.', 'Title': 'COO', 'City': 'Los Angeles', 'State': 'California', 'Email': 'samanthab@dx-email.com', 'Skype': 'samanthab_DX_skype', 'Mobile_Phone': '(213) 555-2858', 'Birth_Date': '1966-05-02', 'Hire_Date': '2004-05-24' }, { 'ID': 3, 'Head_ID': 1, 'Full_Name': 'Arthur Miller', 'Prefix': 'Mr.', 'Title': 'CTO', 'City': 'Denver', 'State': 'Colorado', 'Email': 'arthurm@dx-email.com', 'Skype': 'arthurm_DX_skype', 'Mobile_Phone': '(310) 555-8583', 'Birth_Date': '1972-07-11', 'Hire_Date': '2007-12-18' }, { 'ID': 4, 'Head_ID': 1, 'Full_Name': 'Robert Reagan', 'Prefix': 'Mr.', 'Title': 'CMO', 'City': 'Bentonville', 'State': 'Arkansas', 'Email': 'robertr@dx-email.com', 'Skype': 'robertr_DX_skype', 'Mobile_Phone': '(818) 555-2387', 'Birth_Date': '1974-09-07', 'Hire_Date': '2002-11-08' }, { 'ID': 5, 'Head_ID': 1, 'Full_Name': 'Greta Sims', 'Prefix': 'Ms.', 'Title': 'HR Manager', 'City': 'Atlanta', 'State': 'Georgia', 'Email': 'gretas@dx-email.com', 'Skype': 'gretas_DX_skype', 'Mobile_Phone': '(818) 555-6546', 'Birth_Date': '1977-11-22', 'Hire_Date': '1998-04-23' }, { 'ID': 6, 'Head_ID': 3, 'Full_Name': 'Brett Wade', 'Prefix': 'Mr.', 'Title': 'IT Manager', 'City': 'Reno', 'State': 'Nevada', 'Email': 'brettw@dx-email.com', 'Skype': 'brettw_DX_skype', 'Mobile_Phone': '(626) 555-0358', 'Birth_Date': '1968-12-01', 'Hire_Date': '2009-03-06' }, { 'ID': 7, 'Head_ID': 5, 'Full_Name': 'Sandra Johnson', 'Prefix': 'Mrs.', 'Title': 'Controller', 'City': 'Beaver', 'State': 'Utah', 'Email': 'sandraj@dx-email.com', 'Skype': 'sandraj_DX_skype', 'Mobile_Phone': '(562) 555-2082', 'Birth_Date': '1974-11-15', 'Hire_Date': '2005-05-11' }, { 'ID': 8, 'Head_ID': 4, 'Full_Name': 'Ed Holmes', 'Prefix': 'Dr.', 'Title': 'Sales Manager', 'City': 'Malibu', 'State': 'California', 'Email': 'edwardh@dx-email.com', 'Skype': 'edwardh_DX_skype', 'Mobile_Phone': '(310) 555-1288', 'Birth_Date': '1973-07-14', 'Hire_Date': '2005-06-19' }, { 'ID': 9, 'Head_ID': 3, 'Full_Name': 'Barb Banks', 'Prefix': 'Mrs.', 'Title': 'Support Manager', 'City': 'Phoenix', 'State': 'Arizona', 'Email': 'barbarab@dx-email.com', 'Skype': 'barbarab_DX_skype', 'Mobile_Phone': '(310) 555-3355', 'Birth_Date': '1979-04-14', 'Hire_Date': '2002-08-07' }, { 'ID': 10, 'Head_ID': 2, 'Full_Name': 'Kevin Carter', 'Prefix': 'Mr.', 'Title': 'Shipping Manager', 'City': 'San Diego', 'State': 'California', 'Email': 'kevinc@dx-email.com', 'Skype': 'kevinc_DX_skype', 'Mobile_Phone': '(213) 555-2840', 'Birth_Date': '1978-01-09', 'Hire_Date': '2009-08-11' }, { 'ID': 11, 'Head_ID': 5, 'Full_Name': 'Cindy Stanwick', 'Prefix': 'Ms.', 'Title': 'HR Assistant', 'City': 'Little Rock', 'State': 'Arkansas', 'Email': 'cindys@dx-email.com', 'Skype': 'cindys_DX_skype', 'Mobile_Phone': '(818) 555-6655', 'Birth_Date': '1985-06-05', 'Hire_Date': '2008-03-24' }, { 'ID': 12, 'Head_ID': 8, 'Full_Name': 'Sammy Hill', 'Prefix': 'Mr.', 'Title': 'Sales Assistant', 'City': 'Pasadena', 'State': 'California', 'Email': 'sammyh@dx-email.com', 'Skype': 'sammyh_DX_skype', 'Mobile_Phone': '(626) 555-7292', 'Birth_Date': '1984-02-17', 'Hire_Date': '2012-02-01' }, { 'ID': 13, 'Head_ID': 10, 'Full_Name': 'Davey Jones', 'Prefix': 'Mr.', 'Title': 'Shipping Assistant', 'City': 'Pasadena', 'State': 'California', 'Email': 'davidj@dx-email.com', 'Skype': 'davidj_DX_skype', 'Mobile_Phone': '(626) 555-0281', 'Birth_Date': '1983-03-06', 'Hire_Date': '2011-04-24' }, { 'ID': 14, 'Head_ID': 10, 'Full_Name': 'Victor Norris', 'Prefix': 'Mr.', 'Title': 'Shipping Assistant', 'City': 'Little Rock', 'State': 'Arkansas', 'Email': 'victorn@dx-email.com', 'Skype': 'victorn_DX_skype', 'Mobile_Phone': '(213) 555-9278', 'Birth_Date': '1986-07-23', 'Hire_Date': '2012-07-23' }, { 'ID': 15, 'Head_ID': 10, 'Full_Name': 'Mary Stern', 'Prefix': 'Ms.', 'Title': 'Shipping Assistant', 'City': 'Beaver', 'State': 'Utah', 'Email': 'marys@dx-email.com', 'Skype': 'marys_DX_skype', 'Mobile_Phone': '(818) 555-7857', 'Birth_Date': '1982-04-08', 'Hire_Date': '2012-08-12' }, { 'ID': 16, 'Head_ID': 10, 'Full_Name': 'Robin Cosworth', 'Prefix': 'Mrs.', 'Title': 'Shipping Assistant', 'City': 'Los Angeles', 'State': 'California', 'Email': 'robinc@dx-email.com', 'Skype': 'robinc_DX_skype', 'Mobile_Phone': '(818) 555-0942', 'Birth_Date': '1981-06-12', 'Hire_Date': '2012-09-01' }, { 'ID': 17, 'Head_ID': 9, 'Full_Name': 'Kelly Rodriguez', 'Prefix': 'Ms.', 'Title': 'Support Assistant', 'City': 'Boise', 'State': 'Idaho', 'Email': 'kellyr@dx-email.com', 'Skype': 'kellyr_DX_skype', 'Mobile_Phone': '(818) 555-9248', 'Birth_Date': '1988-05-11', 'Hire_Date': '2012-10-13' }, { 'ID': 18, 'Head_ID': 9, 'Full_Name': 'James Anderson', 'Prefix': 'Mr.', 'Title': 'Support Assistant', 'City': 'Atlanta', 'State': 'Georgia', 'Email': 'jamesa@dx-email.com', 'Skype': 'jamesa_DX_skype', 'Mobile_Phone': '(323) 555-4702', 'Birth_Date': '1987-01-29', 'Hire_Date': '2012-10-18' }, { 'ID': 19, 'Head_ID': 9, 'Full_Name': 'Antony Remmen', 'Prefix': 'Mr.', 'Title': 'Support Assistant', 'City': 'Boise', 'State': 'Idaho', 'Email': 'anthonyr@dx-email.com', 'Skype': 'anthonyr_DX_skype', 'Mobile_Phone': '(310) 555-6625', 'Birth_Date': '1986-02-19', 'Hire_Date': '2013-01-19' }, { 'ID': 20, 'Head_ID': 8, 'Full_Name': 'Olivia Peyton', 'Prefix': 'Mrs.', 'Title': 'Sales Assistant', 'City': 'Atlanta', 'State': 'Georgia', 'Email': 'oliviap@dx-email.com', 'Skype': 'oliviap_DX_skype', 'Mobile_Phone': '(310) 555-2728', 'Birth_Date': '1981-06-03', 'Hire_Date': '2012-05-14' }, { 'ID': 21, 'Head_ID': 6, 'Full_Name': 'Taylor Riley', 'Prefix': 'Mr.', 'Title': 'Network Admin', 'City': 'San Jose', 'State': 'California', 'Email': 'taylorr@dx-email.com', 'Skype': 'taylorr_DX_skype', 'Mobile_Phone': '(310) 555-7276', 'Birth_Date': '1982-08-14', 'Hire_Date': '2012-04-14' }, { 'ID': 22, 'Head_ID': 6, 'Full_Name': 'Amelia Harper', 'Prefix': 'Mrs.', 'Title': 'Network Admin', 'City': 'Los Angeles', 'State': 'California', 'Email': 'ameliah@dx-email.com', 'Skype': 'ameliah_DX_skype', 'Mobile_Phone': '(213) 555-4276', 'Birth_Date': '1983-11-19', 'Hire_Date': '2011-02-10' }, { 'ID': 23, 'Head_ID': 6, 'Full_Name': 'Wally Hobbs', 'Prefix': 'Mr.', 'Title': 'Programmer', 'City': 'Chatsworth', 'State': 'California', 'Email': 'wallyh@dx-email.com', 'Skype': 'wallyh_DX_skype', 'Mobile_Phone': '(818) 555-8872', 'Birth_Date': '1984-12-24', 'Hire_Date': '2011-02-17' }, { 'ID': 24, 'Head_ID': 6, 'Full_Name': 'Brad Jameson', 'Prefix': 'Mr.', 'Title': 'Programmer', 'City': 'San Fernando', 'State': 'California', 'Email': 'bradleyj@dx-email.com', 'Skype': 'bradleyj_DX_skype', 'Mobile_Phone': '(818) 555-4646', 'Birth_Date': '1988-10-12', 'Hire_Date': '2011-03-02' }, { 'ID': 25, 'Head_ID': 6, 'Full_Name': 'Karen Goodson', 'Prefix': 'Miss', 'Title': 'Programmer', 'City': 'South Pasadena', 'State': 'California', 'Email': 'kareng@dx-email.com', 'Skype': 'kareng_DX_skype', 'Mobile_Phone': '(626) 555-0908', 'Birth_Date': '1987-04-26', 'Hire_Date': '2011-03-14' }, { 'ID': 26, 'Head_ID': 5, 'Full_Name': 'Marcus Orbison', 'Prefix': 'Mr.', 'Title': 'Travel Coordinator', 'City': 'Los Angeles', 'State': 'California', 'Email': 'marcuso@dx-email.com', 'Skype': 'marcuso_DX_skype', 'Mobile_Phone': '(213) 555-7098', 'Birth_Date': '1982-03-02', 'Hire_Date': '2005-05-19' }]; export default { getEmployees() { return employees; } };
System.config({ transpiler: 'plugin-babel', meta: { '*.vue': { loader: 'vue-loader' }, }, paths: { 'npm:': 'https://unpkg.com/' }, map: { 'vue': 'npm:vue@3.0.0/dist/vue.esm-browser.js', 'vue-loader': 'npm:dx-systemjs-vue-browser@1.0.15/index.js', 'mitt': 'npm:mitt/dist/mitt.umd.js', 'rrule': 'npm:rrule@2.6.6/dist/es5/rrule.js', 'luxon': 'npm:luxon@1.25.0/build/global/luxon.min.js', 'es6-object-assign': 'npm:es6-object-assign@1.1.0', 'devextreme': 'npm:devextreme@20.2.3', 'devextreme-vue': 'npm:devextreme-vue@20.2.3', 'jszip': 'npm:jszip@3.5.0/dist/jszip.min.js', 'devextreme-quill': 'npm:devextreme-quill@0.9.5/dist/dx-quill.min.js', 'devexpress-diagram': 'npm:devexpress-diagram@2.0.0/dist/dx-diagram.js', 'devexpress-gantt': 'npm:devexpress-gantt@2.0.0/dist/dx-gantt.js', 'preact': 'npm:preact@10.5.5/dist/preact.js', 'preact/hooks': 'npm:preact@10.5.5/hooks/dist/hooks.js', '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' }, packages: { 'devextreme-vue': { main: 'index.js' }, 'devextreme': { defaultExtension: 'js' }, 'devextreme/events/utils': { main: 'index' }, 'devextreme/events': { main: 'index' }, 'es6-object-assign': { main: './index.js', defaultExtension: 'js' } }, babelOptions: { sourceMaps: false, stage0: true } });