Your search did not match any results.

Diagram - Templates with Editing

In this demo, the CustomShapeTemplate method 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 from the data source. The Diagram component reloads modified diagram data whenever the data source changes.

The OnRequestLayoutUpdate function specifies whether the component must reapply its auto layout once the diagram is reloaded.

The CustomDataExpr method links custom employee information from the data source to diagram nodes. Changes made to data are reflected in the diagram's history. Undo and redo actions (available within the control's UI) allow users to rollback/reapply changes.

The CustomShapeToolboxTemplate method specifies the template used for a shape within the toolbox.

Backend API
@model IEnumerable<DevExtreme.NETCore.Demos.Models.OrgItemPlain> @(Html.DevExtreme().Diagram() .ID("diagram") .OnRequestLayoutUpdate("onRequestLayoutUpdate") .CustomShapes(cs => { cs.Add() .Type("employee") .Category("employee") .BaseType("rectangle") .Title("New Employee") .DefaultWidth(1.5) .DefaultHeight(1) .ToolboxWidthToHeightRatio(2) .MinWidth(1.5) .MinHeight(1) .MaxWidth(3) .MaxHeight(2) .AllowEditText(false); }) .CustomShapeTemplate(@<text> <svg class="template"> <text class="template-name" x="50%" y="20%"><%- dataItem ? dataItem.FullName : "Employee's Name" %></text> <text class="template-title" x="50%" y="45%"><%- dataItem ? dataItem.Title : "Employee's Title" %></text> <text class="template-button" x="40%" y="85%" onclick="editEmployee(<%- dataItem && JSON.stringify(dataItem) %>)">Edit</text> <text class="template-button" x="62%" y="85%" onclick="deleteEmployee(<%- dataItem && JSON.stringify(dataItem) %>)">Delete</text> </svg> </text>) .CustomShapeToolboxTemplate(@<text> <svg class="template"> <text x="50%" y="40%">New</text> <text x="50%" y="70%">Employee</text> </svg> </text>) .Nodes(ns => ns .DataSource(d => d .Array() .Key("ID") .Data(Model) .OnInserting("onNodeInserting") ) .KeyExpr("ID") .TypeExpr(new JS("itemTypeExpr")) .CustomDataExpr(new JS("itemCustomDataExpr")) .ParentKeyExpr("HeadID") .AutoLayout(al => al .Type(DiagramDataLayoutType.Tree) ) ) .ContextToolbox(ct => ct .ShapeIconsPerRow(1) .Width(100) ) .Toolbox(tb => tb .Groups(g => { g.Add().Category("employee").Title("Employee").Expanded(true); }) .ShowSearch(false) .ShapeIconsPerRow(1) ) .PropertiesPanel(pp => pp .Tabs(t => { t.Add() .Groups(g => { g.Add() .Title("Page Properties") .Commands(new[] { DiagramCommand.PageSize, DiagramCommand.PageOrientation, DiagramCommand.PageColor }); }); }) ) ) @(Html.DevExtreme().Popup() .ID("popup") .Width(400) .Height(480) .ShowTitle(true) .Title("Edit Employee") .Visible(false) .DragEnabled(false) .ContentTemplate(new TemplateName("popup-content")) ) @using(Html.DevExtreme().NamedTemplate("popup-content")) { <div class="dx-fieldset"> <div class="dx-field"> <div class="dx-field-label">Name</div> <div class="dx-field-value" data-field="FullName"> @(Html.DevExtreme().TextBox().InputAttr("aria-label", "Full Name")) </div> </div> <div class="dx-field"> <div class="dx-field-label">Title</div> <div class="dx-field-value" data-field="Title"> @(Html.DevExtreme().TextBox().InputAttr("aria-label", "Title")) </div> </div> <div class="dx-field"> <div class="dx-field-label">City</div> <div class="dx-field-value" data-field="City"> @(Html.DevExtreme().TextBox().InputAttr("aria-label", "City")) </div> </div> <div class="dx-field"> <div class="dx-field-label">State</div> <div class="dx-field-value" data-field="State"> @(Html.DevExtreme().TextBox().InputAttr("aria-label", "State")) </div> </div> <div class="dx-field"> <div class="dx-field-label">Email</div> <div class="dx-field-value" data-field="Email"> @(Html.DevExtreme().TextBox().InputAttr("aria-label", "Email")) </div> </div> <div class="dx-field"> <div class="dx-field-label">Skype</div> <div class="dx-field-value" data-field="Skype"> @(Html.DevExtreme().TextBox().InputAttr("aria-label", "Skype")) </div> </div> <div class="dx-field"> <div class="dx-field-label">Phone</div> <div class="dx-field-value" data-field="MobilePhone"> @(Html.DevExtreme().TextBox().InputAttr("aria-label", "Phone")) </div> </div> </div> <div class="dx-fieldset buttons"> @(Html.DevExtreme().Button() .Text("Update") .Type(ButtonType.Default) .OnClick("updateEmployee") ) @(Html.DevExtreme().Button() .Text("Cancel") .OnClick("cancelEditEmployee") ) </div> } <script type="text/javascript"> var currentEmployee = {}; var generatedID = 100; function onNodeInserting(values) { values.ID = values.ID || generatedID++; values.FullName = values.FullName || "Employee's Name"; values.Title = values.Title || "Employee's Title"; } function itemTypeExpr(obj) { return "employee"; } function itemCustomDataExpr(obj, value) { if(value === undefined) { return { "FullName": obj.FullName, "Prefix": obj.Prefix, "Title": obj.Title, "City": obj.City, "State": obj.State, "Email": obj.Email, "Skype": obj.Skype, "MobilePhone": obj.MobilePhone }; } else { obj.FullName = value.FullName; 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.MobilePhone = value.MobilePhone; } } function 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.HeadID !== undefined && e.changes[i].data.HeadID !== null) e.allowed = true; } } function editEmployee(employee) { currentEmployee = Object.assign({}, employee); var popup = $("#popup").dxPopup("instance"); popup.show(); popup.content().find(".dx-field-value").each(function() { var field = $(this).attr("data-field"); var edit = $(this).children().dxTextBox("instance"); edit.option({ value: currentEmployee[field], onValueChanged: function(e) { handleChange(field, e.value); } }); }); }; function deleteEmployee(employee) { var diagram = $("#diagram").dxDiagram("instance"); diagram.getNodeDataSource().store().push([{ type: 'remove', key: employee.ID }]); }; function updateEmployee() { var diagram = $("#diagram").dxDiagram("instance"); diagram.getNodeDataSource().store().push([{ type: 'update', key: currentEmployee.ID, data: { "FullName": currentEmployee.FullName, "Title": currentEmployee.Title, "City": currentEmployee.City, "State": currentEmployee.State, "Email": currentEmployee.Email, "Skype": currentEmployee.Skype, "MobilePhone": currentEmployee.MobilePhone } }]); var popup = $("#popup").dxPopup("instance"); popup.hide(); }; function cancelEditEmployee() { currentEmployee = {}; var popup = $("#popup").dxPopup("instance"); popup.hide(); } function handleChange(field, value) { currentEmployee[field] = value; } </script>
using DevExtreme.NETCore.Demos.Models.SampleData; using Microsoft.AspNetCore.Mvc; namespace DevExtreme.NETCore.Demos.Controllers { public class DiagramController : Controller { public ActionResult CustomShapesWithTemplatesWithEditing() { return View(SampleData.OrgItemsPlain); } } }
#diagram { height: 725px; } #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; } .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; }