JavaScript/jQuery Diagram - Restrict Operations
The Diagram UI component raises the requestEditOperation event every time a user attempts an edit operation. This article contains code samples that demonstrate how to use this event's parameters to prohibit individual edit operations and customize a shape collection in the toolbox and context toolbox.
Refer to the following section for more information on the requestEditOperation event's parameters: Prohibit Individual Operations.
Prohibit Creating Loops
The example below demonstrates how to prevent users from connecting a shape to itself:
jQuery
$(function() { var diagram = $("#diagram").dxDiagram({ onRequestEditOperation: function(e) { if (e.operation === "changeConnection") if (e.args.connector && e.args.connector.fromId === e.args.connector.toId) e.allowed = false; }, }).dxDiagram("instance"); });
Angular
<dx-diagram #diagram id="diagram" (onRequestEditOperation)="requestEditOperationHandler($event)"> </dx-diagram>
import { Component, ViewChild} from '@angular/core'; import { DxDiagramModule, DxDiagramComponent } from 'devextreme-angular'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { @ViewChild(DxDiagramComponent, { static: false }) diagram: DxDiagramComponent; requestEditOperationHandler(e) { if (e.operation === "changeConnection") if (e.args.connector && e.args.connector.fromId === e.args.connector.toId) e.allowed = false; } }
Vue
<template> <DxDiagram id="diagram" ref="diagram" @request-edit-operation="onRequestEditOperation"> </DxDiagram> </template> <script> import DxDiagram from 'devextreme-vue/diagram'; export default { components: { DxDiagram, }, methods: { onRequestEditOperation(e) { if (e.operation === "changeConnection") if (e.args.connector && e.args.connector.fromId === e.args.connector.toId) e.allowed = false; }, } }; </script>
React
import React from 'react'; import Diagram from 'devextreme-react/diagram'; class App extends React.Component { constructor(props) { super(props); this.diagramRef = React.createRef(); this.onRequestEditOperation = this.onRequestEditOperation.bind(this); } onRequestEditOperation(e) { if (e.operation === 'changeConnection') if (e.args.connector && e.args.connector.fromId === e.args.connector.toId) e.allowed = false; } render() { return ( <Diagram id="diagram" ref={this.diagramRef} onRequestEditOperation={this.onRequestEditOperation}> </Diagram> ); } } export default App;
Prohibit Adding Shapes Twice
The example below demonstrates how to prevent users from adding more than one shape of each type to a chart:
jQuery
$(function() { var diagram = $("#diagram").dxDiagram({ onRequestEditOperation(e) { if (e.operation === 'addShape') { // Gets types of shapes the chart contains var itemsTypes = e.component.getItems().filter(function(item) { return (item.itemType === "shape") && (item.id !== e.args.shape.id); }).map(a => a.type); // Cancels the operation if the chart contains a shape with the same type as the shape that is about to be added if (itemsTypes.indexOf(e.args.shape.type) !== -1) { e.allowed = false; return; } } }, }).dxDiagram("instance"); });
Angular
<dx-diagram #diagram id="diagram" (onRequestEditOperation)="requestEditOperation($event)"> </dx-diagram>
import { Component, ViewChild} from '@angular/core'; import { DxDiagramModule, DxDiagramComponent } from 'devextreme-angular'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { @ViewChild(DxDiagramComponent, { static: false }) diagram: DxDiagramComponent; requestEditOperation(e) { if (e.operation === 'addShape') { // Gets types of shapes the chart contains var itemsTypes = e.component.getItems().filter(function(item) { return (item.itemType === "shape") && (item.id !== e.args.shape.id); }).map(a => a.type); // Cancels the operation if the chart contains a shape with the same type as the shape that is about to be added if (itemsTypes.indexOf(e.args.shape.type) !== -1) { e.allowed = false; return; } } } }
Vue
<template> <DxDiagram id="diagram" ref="diagram" @request-edit-operation="onRequestEditOperation"> </DxDiagram> </template> <script> import DxDiagram from 'devextreme-vue/diagram'; export default { components: { DxDiagram }, methods: { onRequestEditOperation(e) { if (e.operation === 'addShape') { // Gets types of shapes the chart contains var itemsTypes = e.component.getItems().filter(function(item) { return (item.itemType === "shape") && (item.id !== e.args.shape.id); }).map(a => a.type); // Cancels the operation if the chart contains a shape with the same type as the shape that is about to be added if (itemsTypes.indexOf(e.args.shape.type) !== -1) { e.allowed = false; return; } } }, } }; </script>
React
import React from 'react'; import Diagram, { ContextToolbox,} from 'devextreme-react/diagram'; var currentShapeId; class App extends React.Component { constructor(props) { super(props); this.diagramRef = React.createRef(); this.onRequestEditOperation = this.onRequestEditOperation.bind(this); } onRequestEditOperation(e) { if (e.operation === 'addShape') { // Gets types of shapes the chart contains var itemsTypes = e.component.getItems().filter(function(item) { return (item.itemType === "shape") && (item.id !== e.args.shape.id); }).map(a => a.type); // Cancels the operation if the chart contains a shape with the same type as the shape that is about to be added if (itemsTypes.indexOf(e.args.shape.type) !== -1) { e.allowed = false; return; } } } render() { return ( <Diagram id="diagram" ref={this.diagramRef} onRequestEditOperation={this.onRequestEditOperation} > </Diagram> ); } } export default App;
See Also
Remove Shapes from Toolboxes
In the example below, the Diagram component updates the shape collection in the toolbox and context toolbox as follows:
- Removes a shape from these toolboxes after a user adds it to a chart
- Returns a shape to these toolboxes after a user deletes it from a chart
jQuery
$(function () { var shapeCount = 0; var diagram = $("#diagram").dxDiagram({ onOptionChanged: function (e) { // Detects changes of the Diagram model if (e.name === "hasChanges" && e.value) { e.component.option("hasChanges", false); var currentShapeCount = e.component.getItems().filter(function(item) { return (item.itemType ==="shape") }).length; // Updates the toolbox and context toolbox if a shape was added or deleted if (shapeCount !== currentShapeCount) { shapeCount = currentShapeCount; window.setTimeout(function() { e.component.updateToolbox(); }, 0); } } }, onRequestEditOperation(e) { if (e.operation === "addShapeFromToolbox") { e.component.getItems().forEach(function(item) { // Removes a shape from the toolboxes if the chart contains a shape of this type if (item.itemType === "shape" && item.type === e.args.shapeType) e.allowed = false; }); } }, }) .dxDiagram("instance"); });
Angular
<dx-diagram #diagram id="diagram" (onOptionChanged)="optionChanged($event)" (onRequestEditOperation)="requestEditOperation($event)"> </dx-diagram>
import { Component, ViewChild} from '@angular/core'; import { DxDiagramModule, DxDiagramComponent } from 'devextreme-angular'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { @ViewChild(DxDiagramComponent, { static: false }) diagram: DxDiagramComponent; shapeCount: any; optionChanged(e) { // Detects changes of the Diagram model if (e.name === "hasChanges" && e.value) { e.component.option("hasChanges", false); var currentShapeCount = e.component.getItems().filter(function(item) { return (item.itemType ==="shape") }).length; // Updates the toolbox and context toolbox if a shape was added or deleted if (this.shapeCount !== currentShapeCount) { this.shapeCount = currentShapeCount; window.setTimeout(function() { e.component.updateToolbox(); }, 0); } } } requestEditOperation(e) { if (e.operation === "addShapeFromToolbox") { e.component.getItems().forEach(function(item) { // Removes a shape from the toolboxes if the chart contains a shape of this type if (item.itemType === "shape" && item.type === e.args.shapeType) e.allowed = false; }); } } }
Vue
<template> <DxDiagram id="diagram" ref="diagram" @request-edit-operation="onRequestEditOperation" @option-changed="onOptionChanged"> </DxDiagram> </template> <script> import DxDiagram from 'devextreme-vue/diagram'; var shapeCount = 0; export default { components: { DxDiagram }, methods: { onOptionChanged(e) { // Detects changes of the Diagram model if (e.name === "hasChanges" && e.value) { e.component.option("hasChanges", false); var currentShapeCount = e.component.getItems().filter(function(item) { return (item.itemType ==="shape") }).length; // Updates the toolbox and context toolbox if a shape was added or deleted if (this.shapeCount !== currentShapeCount) { this.shapeCount = currentShapeCount; window.setTimeout(function() { e.component.updateToolbox(); }, 0); } } }, onRequestEditOperation(e) { if (e.operation === "addShapeFromToolbox") { e.component.getItems().forEach(function(item) { // Removes a shape from the toolboxes if the chart contains a shape of this type if (item.itemType === "shape" && item.type === e.args.shapeType) e.allowed = false; }); } }, } }; </script>
React
import React from 'react'; import Diagram from 'devextreme-react/diagram'; var shapeCount; class App extends React.Component { constructor(props) { super(props); this.diagramRef = React.createRef(); this.onRequestEditOperation = this.onRequestEditOperation.bind(this); this.onOptionChanged = this.onOptionChanged.bind(this); } onOptionChanged(e) { // Detects changes of the Diagram model if (e.name === "hasChanges" && e.value) { e.component.option("hasChanges", false); var currentShapeCount = e.component.getItems().filter(function(item) { return (item.itemType ==="shape") }).length; // Updates the toolbox and context toolbox if a shape was added or deleted if (this.shapeCount !== currentShapeCount) { this.shapeCount = currentShapeCount; window.setTimeout(function() { e.component.updateToolbox(); }, 0); } } } onRequestEditOperation(e) { if (e.operation === "addShapeFromToolbox") { e.component.getItems().forEach(function(item) { // Removes a shape from the toolboxes if the chart contains a shape of this type if (item.itemType === "shape" && item.type === e.args.shapeType) e.allowed = false; }); } } render() { return ( <Diagram id="diagram" ref={this.diagramRef} onRequestEditOperation={this.onRequestEditOperation} onOptionChanged={this.onOptionChanged}> </Diagram> ); } } export default App;
See Also
Prohibit Moving Shapes Between Containers
The example below demonstrates how to prevent users from moving a shape from one container to another:
jQuery
$(function() { var containerIds = {}; var diagram = $("#diagram").dxDiagram({ onRequestEditOperation: function(e) { if (e.operation === "moveShape") // Cancels the operation if a user moves a shape outside its container. if (containerIds[e.args.shape.id] !== e.args.shape.containerId) e.allowed = false; }, onSelectionChanged: function(e) { e.component.getItems().forEach(item => {containerIds[item.id] = item.containerId;}); }, }).dxDiagram("instance"); });
Angular
<dx-diagram #diagram id="diagram" (onSelectionChanged)="selectionChanged($event)" (onRequestEditOperation)="requestEditOperation($event)"> </dx-diagram>
import { Component, ViewChild} from '@angular/core'; import { DxDiagramModule, DxDiagramComponent } from 'devextreme-angular'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { @ViewChild(DxDiagramComponent, { static: false }) diagram: DxDiagramComponent; containerIds: any = {}; requestEditOperation(e) { if (e.operation === "moveShape") // Cancels the operation if a user moves a shape outside its container. if (this.containerIds[e.args.shape.id] !== e.args.shape.containerId) e.allowed = false; } selectionChanged(e) { e.component.getItems().forEach(item => { this.containerIds[item.id] = item.containerId; }); } }
Vue
<template> <DxDiagram id="diagram" ref="diagram" @request-edit-operation="onRequestEditOperation" @selection-changed="onSelectionChanged"> </DxDiagram> </template> <script> import DxDiagram from 'devextreme-vue/diagram'; var containerIds = {}; export default { components: { DxDiagram }, methods: { onRequestEditOperation(e) { if (e.operation === "moveShape") // Cancels the operation if a user moves a shape outside its container. if (containerIds[e.args.shape.id] !== e.args.shape.containerId) e.allowed = false; }, onSelectionChanged(e) { e.component.getItems().forEach(item => {containerIds[item.id] = item.containerId;}); } }, }; </script>
React
import React from 'react'; import Diagram, from 'devextreme-react/diagram'; var containerIds = {}; class App extends React.Component { constructor(props) { super(props); this.diagramRef = React.createRef(); this.onRequestEditOperation = this.onRequestEditOperation.bind(this); this.onSelectionChanged = this.onSelectionChanged.bind(this); } onRequestEditOperation(e) { if (e.operation === "moveShape") // Cancels the operation if a user moves a shape outside its container. if (containerIds[e.args.shape.id] !== e.args.shape.containerId) e.allowed = false; } onSelectionChanged(e) { e.component.getItems().forEach(item => {containerIds[item.id] = item.containerId;}); } render() { return ( <Diagram id="diagram" ref={this.diagramRef} onRequestEditOperation={this.onRequestEditOperation} onSelectionChanged={this.onSelectionChanged}> </Diagram> ); } } export default App;
Customize Shape Collection in the Context Toolbox
The following example demonstrates how to hide shapes in the context toolbox depending on the connector's start node type:
jQuery
$(function() { var currentShapeId; var diagram = $("#diagram").dxDiagram({ onRequestEditOperation: function(e) { if (e.operation === "changeConnection" && e.args.connector) // Gets the connector's start node identifier this.currentShapeId = e.args.connector.fromId; if (e.operation === "addShapeFromToolbox") { // Gets the connector's start node type var currentShape = e.component.getItemById(this.currentShapeId); if (e.args.shapeType === "terminator") // If the connector's start node type is "decision" if (currentShape && currentShape.type === "decision") // Hides the "terminator" shape in the context toolbox e.allowed = false; } }, contextToolbox: { enabled: true, shapeIconsPerRow: 3, shapes: [ "process", "decision", "terminator" ], }, }) .dxDiagram("instance"); });
Angular
<dx-diagram #diagram id="diagram" (onRequestEditOperation)="requestEditOperation($event)"> <dxo-context-toolbox [enabled]="true" [shapes]='["process", "decision", "terminator"]' [shapeIconsPerRow]="3"> </dxo-context-toolbox> </dx-diagram>
import { Component, ViewChild} from '@angular/core'; import { DxDiagramModule, DxDiagramComponent } from 'devextreme-angular'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { @ViewChild(DxDiagramComponent, { static: false }) diagram: DxDiagramComponent; currentShapeId : number; requestEditOperation(e) { if (e.operation === "changeConnection" && e.args.connector) // Gets the connector's start node identifier this.currentShapeId = e.args.connector.fromId; if (e.operation === "addShapeFromToolbox") { // Gets the connector's start node type var currentShape = e.component.getItemById(this.currentShapeId); if (e.args.shapeType === "terminator") // If the connector's start node type is "decision" if (currentShape && currentShape.type === "decision") // Hides the "terminator" shape in the context toolbox e.allowed = false; } } }
Vue
<template> <DxDiagram id="diagram" ref="diagram" @request-edit-operation="onRequestEditOperation"> <DxContextToolbox :shape-icons-per-row="3" :width="100" :shapes="['process', 'decision', 'terminator']" /> </DxDiagram> </template> <script> import { DxDiagram, DxContextToolbox} from 'devextreme-vue/diagram'; var currentShapeId; export default { components: { DxDiagram, DxContextToolbox }, methods: { onRequestEditOperation(e) { if (e.operation === "changeConnection" && e.args.connector) // Gets the connector's start node identifier this.currentShapeId = e.args.connector.fromId; if (e.operation === "addShapeFromToolbox") { // Gets the connector's start node type var currentShape = e.component.getItemById(this.currentShapeId); if (e.args.shapeType === "terminator") // If the connector's start node type is "decision" if (currentShape && currentShape.type === "decision") // Hides the "terminator" shape in the context toolbox e.allowed = false; } } } }; </script>
React
import React from 'react'; import Diagram, { ContextToolbox,} from 'devextreme-react/diagram'; var currentShapeId; class App extends React.Component { constructor(props) { super(props); this.diagramRef = React.createRef(); this.onRequestEditOperation = this.onRequestEditOperation.bind(this); } onRequestEditOperation(e) { if (e.operation === 'changeConnection' && e.args.connector) // Gets the connector's start node identifier this.currentShapeId = e.args.connector.fromId; if (e.operation === 'addShapeFromToolbox') { // Gets the connector's start node type var currentShape = e.component.getItemById(this.currentShapeId); if (e.args.shapeType === 'terminator') // If the connector's start node type is "decision" if (currentShape && currentShape.type === 'decision') // Hides the "terminator" shape in the context toolbox e.allowed = false; } } render() { return ( <Diagram id="diagram" ref={this.diagramRef} onRequestEditOperation={this.onRequestEditOperation} > <ContextToolbox shapeIconsPerRow={3} width={100} shapes={['process', 'decision', 'terminator']}> </ContextToolbox> </Diagram> ); } } export default App;
If you have technical questions, please create a support ticket in the DevExpress Support Center.