BPMN text annotation in Vue Diagram component
Text Annotation
-
A BPMN object can be associated with a text annotation which does not affect the flow but gives details about objects within a flow.
-
A TextAnnotation points to or references another BPMN shape, which we call the
textAnnotationTargetof the textAnnotation. When a target shape is moved or deleted, any TextAnnotations attached to the shape will be moved or deleted too. Thus, the TextAnnotations remain with their target shapes though you can reposition the TextAnnotation to any offset from its target. ThetextAnnotationTargetproperty of the BpmnTextAnnotation is used to connect an annotation element to the BPMN Node. -
The annotation element can be switched from a BPMN node to another BPMN node simply by dragging the source end of the annotation connector into the other BPMN node.
-
By default, the TextAnnotation shape has a connection.
-
The
textAnnotationDirectionproperty is used to set the shape direction of the text annotation. -
By default, the
textAnnotationDirectionis set to a Auto. -
To set the size for text annotation, use the
widthandheightproperties of the node. -
The
offsetXandoffsetYproperties are used to set the distance between the BPMN node and the TextAnnotation.
<template>
<div id="app">
<ejs-diagram id="diagram" :width='width' :height='height' :nodes='nodes'></ejs-diagram>
</div>
</template>
<script setup>
import { provide } from "vue";
import { DiagramComponent as EjsDiagram, BpmnDiagrams } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
id: 'event1', style: { strokeWidth: 2 },
height: 70, width: 70, offsetX: 400, offsetY: 200,
shape: {
type: 'Bpmn', shape: 'Event',
event: { event: 'Start', trigger: 'None' },
}
},
//node with target
{
id: 'textNode1', width: 70, height: 70,
offsetX: 400, offsetY: 400,
annotations: [{ content: 'textNode1' }],
shape: {
type: 'Bpmn', shape: 'TextAnnotation',
textAnnotation: { textAnnotationDirection: 'Auto', textAnnotationTarget: 'event1' }
}
},
//Node without target
{
id: 'textNode2', width: 70, height: 70,
offsetX: 600, offsetY: 400,
annotations: [{ content: 'textNode1' }],
shape: {
type: 'Bpmn', shape: 'TextAnnotation',
textAnnotation: { textAnnotationDirection: 'Auto', textAnnotationTarget: '' }
}
},]
const width = "100%";
const height = "600px";
provide('diagram', [BpmnDiagrams]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
</style><template>
<div id="app">
<ejs-diagram id="diagram" :width='width' :height='height' :nodes='nodes'></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent, BpmnDiagrams } from '@syncfusion/ej2-vue-diagrams';
let nodes = [{
id: 'event1', style: { strokeWidth: 2 },
height: 70, width: 70, offsetX: 400, offsetY: 200,
shape: {
type: 'Bpmn', shape: 'Event',
event: { event: 'Start', trigger: 'None' },
}
},
//node with target
{
id: 'textNode1', width: 70, height: 70,
offsetX: 400, offsetY: 400,
annotations: [{ content: 'textNode1' }],
shape: {
type: 'Bpmn', shape: 'TextAnnotation',
textAnnotation: { textAnnotationDirection: 'Auto', textAnnotationTarget: 'event1' }
}
},
//Node without target
{
id: 'textNode2', width: 70, height: 70,
offsetX: 600, offsetY: 400,
annotations: [{ content: 'textNode1' }],
shape: {
type: 'Bpmn', shape: 'TextAnnotation',
textAnnotation: { textAnnotationDirection: 'Auto', textAnnotationTarget: '' }
}
},]
export default {
name: "App",
components: {
"ejs-diagram": DiagramComponent
},
data() {
return {
width: "100%",
height: "600px",
nodes: nodes,
}
},
provide: {
diagram: [BpmnDiagrams]
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
</style>Text annotation in palette.
Text annotation node can be rendered in symbol palette like other bpmn shapes. The following example shows how to render BPMN text annotation node in symbol palette.
<template>
<div style="width: 100%">
<div id="palette-space" class="sb-mobile-palette">
<ejs-symbolpalette id="symbolpalette" :palettes="palettes" :width="palettewidth" :height="paletteheight"
:getNodeDefaults="palettegetNodeDefaults" :getSymbolInfo="getSymbolInfo" :symbolMargin="symbolMargin"
:symbolHeight="symbolHeight" :symbolWidth="symbolWidth">
</ejs-symbolpalette>
</div>
<div id="diagram-space" class="sb-mobile-diagram">
<ejs-diagram style="display: block" ref="diagramObject" id="diagram" :width="width" :height="height">
</ejs-diagram>
</div>
</div>
</template>
<script setup>
import { DiagramComponent as EjsDiagram, SymbolPaletteComponent as EjsSymbolpalette , BpmnDiagrams ,
UndoRedo,SymbolPaletteComponent, DiagramContextMenu, } from '@syncfusion/ej2-vue-diagrams';
import { provide } from "vue";
const bpmnShapes = [
{
id: 'Start',
width: 35,
height: 35,
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'Start' },
},
},
{
id: 'Gateway',
width: 35,
height: 35,
offsetX: 100,
offsetY: 100,
shape: {
type: 'Bpmn',
shape: 'Gateway',
gateway: { type: 'Exclusive' },
},
},
{
id: 'DataObject',
width: 35,
height: 35,
offsetX: 500,
offsetY: 100,
shape: {
type: 'Bpmn',
shape: 'DataObject',
dataObject: { collection: false, type: 'None' },
},
},
{
id: 'textAnnotation',
width: 35,
height: 35,
shape: {
type: 'Bpmn',
shape: 'TextAnnotation',
},
annotations: [{ content: 'textAnnotation' }],
}
];
const width = '100%';
const height = '700px';
const palettes = [
{
id: 'uml',
expanded: true,
symbols: bpmnShapes,
title: 'BPMN Shapes',
},
]
const palettewidth = '100%';
const paletteheight = '200px';
const symbolHeight = 100;
const symbolWidth = 100;
const palettegetNodeDefaults = (symbol) => {
symbol.width = 100;
symbol.height = 100;
}
const symbolMargin = { left: 15, right: 15, top: 15, bottom: 15 };
const getSymbolInfo = (symbol) => {
return { fit: true, description: { text: symbol.id } };
}
provide('diagram', [BpmnDiagrams, UndoRedo, DiagramContextMenu]);
provide('SymbolPalette',[BpmnDiagrams]);
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
</style><template>
<div style="width: 100%">
<div id="palette-space" class="sb-mobile-palette">
<ejs-symbolpalette id="symbolpalette" :palettes="palettes" :width="palettewidth" :height="paletteheight"
:getNodeDefaults="palettegetNodeDefaults" :getSymbolInfo="getSymbolInfo" :symbolMargin="symbolMargin"
:symbolHeight="symbolHeight" :symbolWidth="symbolWidth"></ejs-symbolpalette>
</div>
<div id="diagram-space" class="sb-mobile-diagram">
<ejs-diagram style="display: block" ref="diagramObject" id="diagram" :width="width"
:height="height"></ejs-diagram>
</div>
</div>
</template>
<script>
import { DiagramComponent, BpmnDiagrams,
UndoRedo,SymbolPaletteComponent, DiagramContextMenu,
} from '@syncfusion/ej2-vue-diagrams';
let bpmnShapes = [
{
id: 'Start',
width: 35,
height: 35,
shape: {
type: 'Bpmn',
shape: 'Event',
event: { event: 'Start' },
},
},
{
id: 'Gateway',
width: 35,
height: 35,
offsetX: 100,
offsetY: 100,
shape: {
type: 'Bpmn',
shape: 'Gateway',
gateway: { type: 'Exclusive' },
},
},
{
id: 'DataObject',
width: 35,
height: 35,
offsetX: 500,
offsetY: 100,
shape: {
type: 'Bpmn',
shape: 'DataObject',
dataObject: { collection: false, type: 'None' },
},
},
{
id: 'textAnnotation',
width: 35,
height: 35,
shape: {
type: 'Bpmn',
shape: 'TextAnnotation',
},
annotations: [{ content: 'textAnnotation' }],
}
];
export default {
name: "App",
components: {
"ejs-symbolpalette": SymbolPaletteComponent,
"ejs-diagram": DiagramComponent
},
data() {
return {
width: '100%',
height: '700px',
palettes: [
{
id: 'uml',
expanded: true,
symbols: bpmnShapes,
title: 'Bpmn Shapes',
},
],
palettewidth: '100%',
paletteheight: '200px',
symbolHeight: 100,
symbolWidth: 100,
palettegetNodeDefaults: (symbol) => {
symbol.width = 100;
symbol.height = 100;
},
symbolMargin: { left: 15, right: 15, top: 15, bottom: 15 },
getSymbolInfo: (symbol) => {
return { fit: true, description: { text: symbol.id } };
},
};
},
provide: {
diagram: [BpmnDiagrams, UndoRedo, DiagramContextMenu],
SymbolPalette:[BpmnDiagrams]
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
</style>Connect the TextAnnotation to BPMN node
Drag and drop any bpmn shapes from the palette to diagram and connect the BPMN Node and textAnnotation.
The following image shows how to drag a symbol from the palette and connect the textAnnotation to the BPMNNode with interaction.

Text annotation direction
There are several types of Text annotation directions as follows:
| Text annotation direction | Image |
|---|---|
| Auto | ![]() |
| Left | ![]() |
| Right | ![]() |
| Top | ![]() |
| Bottom | ![]() |
Add text annotation at runtime
Text annotations can be added dynamically using either the addTextAnnotation method or the add method of the diagram. The following example shows how to use these methods to add a text annotation node.
<template>
<div id="app">
<ejs-button ref="textAnnotation" id="textAnnotation">Add Text Annotation</ejs-button>
<ejs-button ref="textAnnotationNode" id="textAnnotationNode">Add Text Annotation Node</ejs-button>
<ejs-button ref="textAnnotationAlone" id="textAnnotationAlone">Add TextAnnotation Alone</ejs-button>
<ejs-diagram
id="diagram"
:width="width"
:height="height"
:nodes="nodes"
ref="diagram"
></ejs-diagram>
</div>
</template>
<script setup>
import { provide } from "vue";
import { ref, onMounted } from "vue";
import {
DiagramComponent as EjsDiagram,
randomId,
BpmnDiagrams,
} from "@syncfusion/ej2-vue-diagrams";
import { ButtonComponent as EjsButton } from "@syncfusion/ej2-vue-buttons";
// Refs for elements and diagram instance
const diagram = ref(null);
const textAnnotation = ref(null);
const textAnnotationNode = ref(null);
const textAnnotationAlone = ref(null);
// Diagram nodes data
const nodes = [
{
id: "event",
offsetX: 200,
offsetY: 200,
width: 70,
height: 70,
shape: { type: "Bpmn", shape: "Event" },
},
];
// Diagram dimensions
const width = "100%";
const height = "600px";
// Utility function to add a text annotation to the diagram
const addTextAnnotation = (textAnnotation, parentNode = null) => {
const diagramInstance = diagram.value.ej2Instances;
// If there's a parent node (like the 'event' node), add the annotation to it
if (parentNode) {
diagramInstance.addTextAnnotation(textAnnotation, parentNode);
} else {
// Otherwise, add the annotation to the diagram as an independent element
diagramInstance.add(textAnnotation);
}
};
provide("diagram", [BpmnDiagrams]);
// Mount lifecycle hook to handle event bindings after component is mounted
onMounted(() => {
const diagramInstance = diagram.value.ej2Instances;
const textAnnotationInstance = textAnnotation.value.ej2Instances;
const textAnnotationNodeInstance = textAnnotationNode.value.ej2Instances;
const textAnnotationAloneInstance = textAnnotationAlone.value.ej2Instances;
// Adding event handlers for button clicks
textAnnotationInstance.element.onclick = () => {
const eventNode = diagramInstance.nodes[0]; // 'event' node
const annotation = {
name: "newAnnotation" + randomId(),
angle: 0,
length: 100,
width: 100,
height: 40,
text: "New Annotation",
};
addTextAnnotation(annotation, eventNode); // Add to 'event' node
};
textAnnotationNodeInstance.element.onclick = () => {
const annotation = {
id: "textAnnotation" + randomId(),
offsetX: 300,
offsetY: 100,
width: 100,
height: 40,
annotations: [{ content: "Text Annotation" }],
shape: {
type: "Bpmn",
shape: "TextAnnotation",
textAnnotation: {
textAnnotationTarget: "event",
textAnnotationDirection: "Auto",
},
},
};
addTextAnnotation(annotation);
};
textAnnotationAloneInstance.element.onclick = () => {
const annotation = {
id: "textAnnotationAlone" + randomId(),
offsetX: 300,
offsetY: 300,
width: 100,
height: 70,
annotations: [{ content: "Text Annotation" }],
shape: {
type: "Bpmn",
shape: "TextAnnotation",
},
};
addTextAnnotation(annotation); // Add to diagram without a parent
};
});
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
</style><template>
<div id="app">
<ejs-button v-on:click="text">Add Text Annotation </ejs-button>
<ejs-button v-on:click="node">Add Text Annotation Node </ejs-button>
<ejs-button v-on:click="alone">Add Text Annotation Alone </ejs-button>
<ejs-diagram
id="diagram"
ref="diagram"
:width="width"
:height="height"
:nodes="nodes"
></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent, BpmnDiagrams,randomId } from '@syncfusion/ej2-vue-diagrams';
import { ButtonComponent } from '@syncfusion/ej2-vue-buttons';
let nodes = [{
id: 'event',
offsetX: 200,
offsetY: 200,
width: 70,
height: 70,
shape: { type: 'Bpmn', shape: 'Event' },
},]
export default {
name: "App",
components: {
"ejs-diagram": DiagramComponent,
'ejs-button': ButtonComponent,
},
data() {
return {
width: "100%",
height: "600px",
nodes: nodes,
}
},
provide: {
diagram: [BpmnDiagrams]
},
methods: {
text: function () {
var diagramInstance = this.$refs.diagram.ej2Instances;
let event = diagramInstance.nodes[0];
let textAnnotation = {
name: 'newAnnotation' + randomId(),
angle: 0,
length: 100,
width: 100,
height: 40,
text: 'New Annotation',
};
/**
* parameter 1 - TextAnnotation to be added
* parameter 2 - The parent node where the text annotation will be added as a child.
*/
diagramInstance.addTextAnnotation(textAnnotation, event);
},
node: function () {
var diagramInstance = this.$refs.diagram.ej2Instances;
let textAnnotation = {
id: 'textAnnotation' + randomId(),
offsetX: 300,
offsetY: 100,
width: 100,
height: 40,
annotations: [{ content: 'Text Annotation' }],
shape: {
type: 'Bpmn',
shape: 'TextAnnotation',
textAnnotation: {
//Parent node of text annotation
textAnnotationTarget: 'event',
textAnnotationDirection: 'Auto',
},
},
};
/**
* parameter 1 - TextAnnotation to be added to the event node
*/
diagramInstance.add(textAnnotation);
},
alone: function () {
var diagramInstance = this.$refs.diagram.ej2Instances;
let textAnnotation = {
id: 'textAnnotationAlone' + randomId(),
offsetX: 300,
offsetY: 300,
width: 100,
height: 70,
annotations: [{ content: 'Text Annotation' }],
shape: {
type: 'Bpmn',
shape: 'TextAnnotation',
},
};
/**
* parameter 1 - TextAnnotation to be added to diagram without parent
*/
diagramInstance.add(textAnnotation);
},
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-navigations/styles/tailwind3.css";
</style>



