Customize the Diagram Component

This article contains code samples that demonstrate how to customize the Diagram UI component and its items.

Change the Component's Background Color

Override the following CSS rule to change the Diagram component's background color:

app.component.css
  • ::ng-deep #diagram .dxdi-canvas .grid {
  • fill: lightGray !important;
  • }

Change the Diagram's background color

Change the Color of Connector Points

Add the following CSS rules to change the color of connector points:

app.component.css
  • ::ng-deep .dx-diagram .dxdi-control .dxdi-canvas .connection-point:not(.selector) {
  • stroke: red;
  • }
  • ::ng-deep .dx-diagram .dxdi-control .dxdi-canvas .connection-point:not(.selector).active {
  • fill: red;
  • }

Change the connector points color

Customize the Context Menu's Items

The example below demonstrates how to show default and custom commands in the context menu depending on the selected item:

app.component.html
app.component.ts
  • <dx-diagram #diagram id="diagram" (onSelectionChanged)="selectionChanged($event)" (onCustomCommand)="customCommand($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;
  • selectionChanged(e) {
  • // Displays the "showGrid" and "snapToGrid" commands if a user selects no items
  • if (e.items.length === 0)
  • e.component.option("contextMenu.commands", ["showGrid", "snapToGrid"]);
  • else
  • // Displays the "fontName", "fontSize", and "selectShapes" commands when a user selects a shape
  • if (e.items[0].itemType === "shape")
  • e.component.option("contextMenu.commands", ["fontName", "fontSize", {name: "selectShapes", text: "Select All Shapes"}]);
  • else
  • // Displays the "connectorLineStart", "connectorLineEnd", and "selectConnectors" commands when a user selects a connector
  • e.component.option("contextMenu.commands", ["connectorLineStart", "connectorLineEnd", {name: "selectConnectors", text: "Select All Connectors"}]);
  • }
  • customCommand(e) {
  • if (e.name == "selectShapes") {
  • var shapes = e.component.getItems().filter(function(item) {
  • return (item.itemType === "shape")
  • });
  • e.component.setSelectedItems(shapes);
  • }
  • if (e.name == "selectConnectors") {
  • var connectors = e.component.getItems().filter(function(item) {
  • return (item.itemType === "connector")
  • });
  • e.component.setSelectedItems(connectors);
  • }
  • }
  • }

Specify a Command's Location on the Main Toolbar

Use the location property to set the location of a command or separator on the main toolbar. This property accepts one of the following values:

  • center
    Places the command in the center of the main toolbar.

  • before
    Places the command before the central element(s).

  • after
    Places the command after the central element(s).

The Diagram UI component displays commands with the same location property value in the order in which you listed them. When the main toolbar cannot accommodate all commands, the component places excess commands in the overflow menu.

The example below demonstrates how to customize the main toolbar:

app.component.html
app.module.ts
  • <dx-diagram #diagram id="diagram" (onCustomCommand)="onCustomCommand($event)">
  • <dxo-main-toolbar [visible]="true">
  • <dxi-command name="undo" location="before"> </dxi-command>
  • <dxi-command name="redo" location="before"> </dxi-command>
  • <dxi-command name="separator" location="before"> </dxi-command>
  • <dxi-command name="copy" location="center"> </dxi-command>
  • <dxi-command name="paste" location="center"> </dxi-command>
  • <dxi-command name="cut" location="center"> </dxi-command>
  • <dxi-command name="separator" location="after"> </dxi-command>
  • <dxi-command
  • name="clear"
  • icon="clearsquare"
  • text="Clear Diagram"
  • location="after">
  • </dxi-command>
  • </dxo-main-toolbar>
  • <dxo-toolbox visibility="hidden"> </dxo-toolbox>
  • <dxo-history-toolbar [visible]="false"> </dxo-history-toolbar>
  • <dxo-view-toolbar [visible]="false"> </dxo-view-toolbar>
  • </dx-diagram>
  • import { NgModule, Component, ViewChild, enableProdMode, } from '@angular/core';
  • import { BrowserModule } from '@angular/platform-browser';
  • import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  • import { HttpClient, HttpClientModule } from '@angular/common/http';
  • import { DxDiagramModule, DxDiagramComponent } from 'devextreme-angular';
  • import dialog from 'devextreme/ui/dialog';
  •  
  • @Component({
  • selector: 'demo-app',
  • templateUrl: 'app/app.component.html',
  • styleUrls: ['app/app.component.css'],
  • preserveWhitespaces: true,
  • })
  • export class AppComponent {
  • @ViewChild(DxDiagramComponent, { static: false }) diagram: DxDiagramComponent;
  •  
  • constructor(http: HttpClient) {
  • http.get('data/diagram-flow.json').subscribe((data) => {
  • this.diagram.instance.import(JSON.stringify(data));
  • }, (err) => {
  • throw 'Data Loading Error';
  • });
  • }
  •  
  • onCustomCommand(e) {
  • if (e.name === 'clear') {
  • const result = dialog.confirm('Are you sure you want to clear the diagram? This action cannot be undone.', 'Warning');
  • result.then(
  • (dialogResult) => {
  • if (dialogResult) {
  • e.component.import('');
  • }
  • },
  • );
  • }
  • }
  • }
  •  
  • @NgModule({
  • imports: [
  • BrowserModule,
  • HttpClientModule,
  • DxDiagramModule,
  • ],
  • declarations: [AppComponent],
  • bootstrap: [AppComponent],
  • })
  • export class AppModule { }
  •  
  • platformBrowserDynamic().bootstrapModule(AppModule);

Customize main toolbar

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:

app.component.html
app.component.ts
  • <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;
  • }
  • }

Prohibit Adding Shapes Twice

The example below demonstrates how to prevent users from adding more than one shape of each type to a chart:

app.component.html
app.component.ts
  • <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;
  • }
  • }
  • }
  • }
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
app.component.html
app.component.ts
  • <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;
  • });
  • }
  • }
  • }
See Also

Prohibit Moving Shapes Between Containers

The example below demonstrates how to prevent users from moving a shape from one container to another:

app.component.html
app.component.ts
  • <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;
  • });
  • }
  • }

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:

app.component.html
app.component.ts
  • <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;
  • }
  • }
  • }