Skip to main content

Vue 3 Integration

JsPlumb has solid integration with Vue 3, shipping in the package @jsplumbtoolkit/browser-ui-vue3.

Vue3 versions of our Flowchart Builder, Schema Builder and Chatbot starter apps are available in the JsPlumb demonstrations organisation on Github.

Imports

"dependencies":{
"@jsplumbtoolkit/browser-ui-vue3":"7.2.0"
}

Setup

You need to import JsPlumb in your Vue bootstrap code:

import { createApp } from 'vue'
import App from './App.vue'

import { JsPlumbToolkitVue3Plugin } from "@jsplumbtoolkit/browser-ui-vue3"

const app = createApp(App)
app.use(JsPlumbToolkitVue3Plugin)

Quick start

Here's a basic setup that uses the SurfaceComponent to render a couple of nodes and an edge:

<template>
<div style="width:100%;height:100%">
<SurfaceComponent :data="this.getData()"/>
<ControlsComponent/>
<MiniviewComponent/>
</div>
</template>
<script>

import { defineComponent } from "vue"

export default defineComponent({
methods:{
getData:function() {
return {
data:{
nodes:[
{ id:"1", label:"1", left:50, top:50},
{ id:"2", label:"TWO", left:250, top:250}
],
edges:[
{ source:"1", target:"2" }
]
}
}
}
}
})

</script>

In the above example the only attribute we set on the Surface was data, meaning the component uses a default Absolute layout and a default component for rendering nodes. The Surface, though, is highly configurable, mainly through its view and renderParams inputs. Here's the same example from before but with a few more things configured, and an Vue 3 component to render nodes with:

NodeComponent.vue
<template>
<div style="width:60px;60px;background-color:white;border:1px solid black;text-align:center">
<h3>{{obj.label}}</h3></div>
</template>
<script>
import { BaseNodeComponent } from '@jsplumbtoolkit/browser-ui-vue3'

export default {
mixins:[BaseNodeComponent]
}
</script>
<template>
<div style="width:100%;height:100%">
<SurfaceComponent :data="this.getData()"
:renderOptions="this.renderParams()"
></SurfaceComponent>
<ControlsComponent/>
<MiniviewComponent/>
</div>
</template>
<script>

import { defineComponent } from "vue"
import NodeComponent from './NodeComponent.vue'

export default defineComponent({
methods:{
getData:function() {
return {
data:{
nodes:[
{ id:"1", label:"1", left:50, top:50},
{ id:"2", label:"TWO", left:250, top:250}
],
edges:[
{ source:"1", target:"2" }
]
}
}
},
renderParams:function() {
return {
layout:{
type:"ForceDirected"
},
grid:{
size:{
w:50, h:50
}
}
}
},
view:function() {
return {
nodes:{
default:{
component:NodeComponent
}
}
}
}
})

</script>

Rendering Nodes and Groups

At the core of your UI will be a SurfaceComponent, which provides the canvas onto which nodes, groups and edges are drawn. In the Quickstart section above we show a couple of examples of the SurfaceComponent in action.

Each node or group in your UI is rendered as an individual component. In the first example above, we rely on JsPlumb's default Vue 3 component to render each node, but in the second we provide a component ourselves, although its quite simple. In a real world app your components will likely be more complex. As an example, consider the component we use to render nodes in the Flowchart Builder:

<script>

import { BaseNodeComponent } from '@jsplumbtoolkit/browser-ui-vue3'

export default {
mixins:[BaseNodeComponent],
props:["shapeLibrary", "anchorPositions"]
}

</script>
<template>
<div :style="{color:obj.textColor}" class="flowchart-object" data-jtk-target="true">
<span>{{obj.text}}</span>
<ShapeComponent :obj="obj" :shape-library="shapeLibrary"></ShapeComponent>
<div v-for="anchor in anchorPositions"
:class="'jtk-connect jtk-connect-' + anchor.id"
:data-jtk-anchor-x="anchor.x"
:data-jtk-anchor-y="anchor.y"
:data-jtk-orientation-x="anchor.ox"
:data-jtk-orientation-y="anchor.oy"
data-jtk-source="true"
data-jtk-port-type="source"></div>
<div class="node-delete node-action delete"></div>
</div>
</template>

Mapping types to components

The viewOptions prop is used to map node/group types to Components, and to certain behaviours. For instance, this is the nodes section of the viewOptions for the flowchart builder starter app:

const view = {
nodes:{
default:{
component:NodeComponent,
// target connections to this node can exist at any of the given anchorPositions
anchorPositions,
// node can support any number of connections.
maxConnections: -1,
events: {
[EVENT_TAP]: (params) => {
edgeEditor.stopEditing()
// if zero nodes currently selected, or the shift key wasnt pressed, make this node the only one in the selection.
if (toolkit.getSelection()._nodes.length < 1 || params.e.shiftKey !== true) {
toolkit.setSelection(params.obj)
} else {
// if multiple nodes already selected, or shift was pressed, add this node to the current selection.
toolkit.addToSelection(params.obj)
}
}
},
inject:{
shapeLibrary:shapeLibrary,
anchorPositions:anchorPositions
}
}
}
}

The component property in each mapping points to some Vue Component. If you omit component from a mapping, JsPlumb will use a default node/group component. This is great for prototyping or for apps whose nodes do not have rich content.

Notice the inject section in the node mapping above: it instructs JsPlumb to inject two props into the NodeComponent when it instantiates one. In this case we pass in a shape library, which we use in this app to render shapes, and a set of anchor positions. You'll see these two props defined on the NodeComponent, whose source is shown above.

BaseNodeComponent

This mixin exposes several helper methods and is responsible for notifying the Vue 3 core when a node has been initially rendered. It is automatically injected into any component used to render nodes.

Methods

  • getNode() Gets the underlying Toolkit node that the component is rendering
  • removeNode() Instructs the Toolkit to remove the node that the component is rendering. This will of course result in the destruction of the component.
  • getToolkit() Gets the underlying Toolkit instance for the node the component is rendering.
  • updateNode(data:any) Updates the underlying node that the component is rendering.

Nodes rendered with this mixin are fully reactive: calls to updateNode will result in a repaint of the component with no further effort involved on your part.

BaseGroupComponent

This mixin exposes several helper methods and is responsible for notifying the Vue 3 core when a group has been initially rendered. It is automatically injected into any component used to render groups.

Methods

  • getGroup() Gets the underlying Toolkit group that the component is rendering
  • removeGroup(removeChildNodes?:boolean) Instructs the Toolkit to remove the group that the component is rendering, and possibly all of the child nodes of the group too. This will of course result in the destruction of the component.
  • getToolkit() Gets the underlying Toolkit instance for the group the component is rendering.
  • updateGroup(data:any) Updates the underlying group that the component is rendering.

Groups rendered with this mixin are fully reactive: calls to updateGroup will result in a repaint of the component with no further effort involved on your part.

Injecting values in nodes/groups

From version 5.13.7 onwards, you can provide a set of data values that you'd like to inject into a component via the inject property of a node definition:

export default {

name: 'jsp-toolkit',
props:["surfaceId"],
data:() => {
renderParams:{ ... },
view:{
nodes:{
default:{
component:MyNode,
inject:{
injectedStaticValue:"My Static Value",
injectedDynamicValue:(node, toolkit) => `My Dynamic Value ${node.id}`
}
}
}
}
}
}

Each value you inject can be either a static value, or a function that takes as argument the current node (or group) and the underlying Toolkit instance, and which returns an appropriate value.

In the component itself you need to declare these in the props:

props:{
injectedStaticValue:String,
injectedDynamicValue:String
},

You can then access these values in your template:

<template>
<div class="injected-static">{{injectedStaticValue}}</div>
<div class="injected-dynamic">{{injectedDynamicValue}}</div>
</template>

Rendering Ports

The Vue 3 integration supports rendering ports. A good example for how to do this can be found in the code for the Schema Builder starter application.


Shape Libraries

A shape library is a set of named shapes that you will use to render SVG inside the vertices in your application. These are discussed in detail in the shape libraries and shape-sets pages; here we provide an overview of how to use these modules in a Vue app.

Creating a shape library

In Vue, a shape library is created the same way as in vanilla js. In the code below we create a shape library with flowchart and basic shapes, which we then inject into our SurfaceComponent via its renderOptions prop:

<script>
import { defineComponent } from "vue";
import { FLOWCHART_SHAPES, BASIC_SHAPES, ShapeLibraryImpl, DEFAULT } from "@jsplumbtoolkit/browser-ui"

const shapeLibrary = new ShapeLibraryImpl([FLOWCHART_SHAPES, BASIC_SHAPES])

export default defineComponent({
name:"flowchart",
methods:{
renderOptions:function() {
return {
shapes:{
library:shapeLibrary
}
}
},
viewOptions:function() {
return {
nodes:{
default:{
component:NodeComponent,
inject:{
shapeLibrary:shapeLibrary
}
}
}
}
}
}
})

</script>
<template>
<div>
<SurfaceComponent :renderOptions="this.renderOptions()"/>
</div>
</template>

Rendering an SVG shape

Shapes can be rendered with a ShapeComponent, which is a component that expect to be given some shape library and the backing data for a vertex. In the code above we show viewOptions with a mapping for nodes, that uses a NodeComponent, into which JsPlumb injects a shapeLibrary prop. Internally, that NodeComponent will use a ShapeComponent to render the SVG:

<script>
import { BaseNodeComponent } from '@jsplumbtoolkit/browser-ui-vue3'

export default {
mixins:[BaseNodeComponent],
props:["shapeLibrary", "anchorPositions"]
}

</script>
<template>
<div>
<span>{{obj.text}}</span>
<ShapeComponent :obj="obj" :shape-library="shapeLibrary"></ShapeComponent>
</div>
</template>

By default the shape component will render just the shape. If you want to add labels you can do so like this:

<script>
import { BaseNodeComponent } from '@jsplumbtoolkit/browser-ui-vue3'

export default {
mixins:[BaseNodeComponent],
props:["shapeLibrary", "anchorPositions"]
}

</script>
<template>
<div>
<span>{{obj.text}}</span>
<ShapeComponent :obj="obj" :shape-library="shapeLibrary" :showLabels="true" labelProperty="text"></ShapeComponent>
</div>
</template>

Adding your own shapes

To add your own shapes, you'll need to create a shape set. These are discussed on a separate page.

Dragging new nodes/groups

Palettes offer a means for your users to drag new nodes/groups onto your canvas. JsPlumb Vue offers a SurfaceDrop mixin to support drag/drop of HTML elements, and a ShapeLibraryPaletteComponent to support drag/drop of SVG shapes.

HTML elements

To configure your UI to drag and drop HTML elements onto your canvas, you'll use a SurfaceDrop mixin. As an example of how to set one up, this is the palette from our Chatbot starter app:


<template>
<div class="jtk-schema-palette">
<div class="jtk-schema-palette-item" :data-type="entry.type"
title="Drag to add new" v-for="entry in data" :key="entry.type">
{{entry.label}}
</div>
</div>
</template>

<script>

import {defineComponent} from "vue"
import { SurfaceDrop } from '@jsplumbtoolkit/browser-ui-vue3'

import { TABLE, VIEW } from "../constants"

export default defineComponent({
mixins:[ SurfaceDrop ],
data:function() {
return {
data:[
{ label:"Table", type:TABLE },
{ label:"View", type:VIEW}
]
};
}
})

</script>

Now we reference this Palette component inside the template of our app, providing a selector, which identifies what child elements should be draggable, and a dataGenerator, which is what JsPlumb uses to get an initial payload when the user starts dragging an element. Here we also show how to setup a dataGenerator - the best way is to declare it in the data section of your component as the template can easily reference it:

<script>

export default defineComponent({
name:"FlowchartBuilder",
methods:{ ... },
mounted() { }
})

</script>
<template>
...
<Palette selector="[data-type]"/>
...
</template>

Selector

Note that we used a selector of [data-type] in this app - meaning, some element that has an data-type attribute. You can use any valid CSS3 selector.

Valid markup

In our Palette we use a div for each draggable type, inside an element with flex-column set on it. JsPlumb is very flexible, though - you can use any markup you like.

Surface ID

The SurfaceDrop needs to know which surface it is going to be attached to, which is not shown in the code above. This is because from 6.70.0 onwards, when you omit the surfaceId from a component, JsPlumb uses a default value. If we had a UI with more than one surface we could assign it an ID and then also tell our palette about it:

<template>
...
<Palette selector="[data-type]" :dataGenerator="dataGenerator"
surfaceId="myOtherSurface"/>
...
</template>

SVG shapes

In JsPlumb Vue, you can use a ShapePaletteComponent. This is how you'd include it in a template:

<template>
<div id="app">

<SurfaceComponent :renderOptions="this.renderOptions()"
:viewOptions="this.viewOptions()"
url="copyright.json">
</SurfaceComponent>

<ShapePaletteComponent :data-generator="dataGenerator" initial-set="flowchart"></ShapePaletteComponent>

</div>
</template>

The :data-generator prop is a function used to get an initial dataset when a user starts to drag a shape. You can see this defined in the methods of the component code below.

Other important points to note:

  • The shapeLibrary is supplied in the renderOptions which are passed to the SurfaceComponent. The ShapePaletteComponent retrieves the shape library from the surface, so don't forget this step.

  • We inject the shapeLibrary into NodeComponent in the viewOptions. NodeComponent declares a shapeLibrary prop (shown below), which it then uses to draw shapes.

<script>
import NodeComponent from './Node.vue'
import { defineComponent } from "vue";

import {
FLOWCHART_SHAPES, BASIC_SHAPES,
ShapeLibraryImpl

} from "@jsplumbtoolkit/browser-ui"

const shapeLibrary = new ShapeLibraryImpl([FLOWCHART_SHAPES, BASIC_SHAPES])

export default defineComponent({
methods:{
viewOptions:function() {
return {
nodes:{
default:{
component:NodeComponent,
inject:{
shapeLibrary:shapeLibrary
}
}
}
}
},
renderOptions:function() {
return {
shapes:{
library:shapeLibrary
}
}
}
},
data:() => {
return {
shapeLibrary,
dataGenerator:(el) => {
return {
textColor:"#FF0000",
outline:"#000000",
fill:"#FFFFFF",
text:'Hello',
outlineWidth:2
}
}
}
}
})
</script>

Component List

This is a full list of the components that are available in JsPlumb Vue. For each we provide a sample usage, which does not necessarily use all of the props available for the given component.

SurfaceComponent

This component provides an instance of JsPlumb and a surface widget to render the contents. You do not instantiate either the JsPlumb Toolkit or the Surface yourself, the Vue 3 code handles that. If you subsequently want to access the Toolkit instance a good approach is to declare a ref for the Toolkit component as shown below. After the example follows a brief discussion of how to get access to the Toolkit and to the Surface.

Example

<SurfaceComponent 
ref="toolkitComponent"
:renderOptions="this.renderOptions()"
:toolkitOptions="this.toolkitOptions()"
:viewOptions="this.viewOptions()">
</SurfaceComponent>

Props

All props are optional.

  • id Unique ID for the Toolkit instance. Can be used to retrieve a Toolkit instance from the jsPlumbToolkitVue3 module.
  • surface-id Unique ID for the Surface widget. Required if you wish to attach a Miniview or a Palette. Also useful if you wish to interact with a Surface, to perform operations such as zooming, centering on content, etc.
  • renderOptions Parameters to pass in to the constructor of the Surface widget. Note here we use the v-bind: prefix to tell Vue that the object we are injecting is in the Vue instance's model.
  • toolkitOptions Parameters to pass in to the constructor of the Toolkit instance. Note again the use of v-bind: in our example above.
  • viewOptions View parameters. Views are discussed here.

In this example we supply renderOptions, toolkitOptions and viewOptions to the return value of methods - the underlying code looks like this:

export default defineComponent({
name: 'some-component',
props:["surfaceId"],
methods:{
viewOptions:function() {
return {
nodes: {
"start": {
component:StartNode
},
...other node types
},
... rest of the view
}
},
toolkitOptions:function() {
return {
beforeStartConnect: (node) => {
// limit edges from start node to 1. if any other type of node, return
return (node.data.type === START && node.getEdges().length > 0) ? false : {label: "..."};
}
}
},
renderOptions:function() {
return {
layout:{
type:ForceDirectedLayout.type
},
... other render params.
]
}
}
})

Accessing the Toolkit and the Surface

You'll almost certainly want to access the underlying Toolkit instance, which is best done by declaring a ref as shown above. This ref can be accessed when the component mounts, as in the snippet below. We also show here how to access a surface, which you do via the loadSurface method from the Vue 3 integration package:


import { loadSurface } from '@jsplumbtoolkit/browser-ui-vue3';

let toolkit

export default defineComponent({

mounted() {
toolkit = this.$refs.toolkitComponent.toolkit
loadSurface((s) => {
// s is of type Surface.
})
}
})

loadSurface takes a callback rather than passing the surface back directly. This is because there is no guarantee that a surface with the given ID exists - it may be not loaded yet. If you try to access a surface that is not yet loaded, your request is queued, and then subsequently when a surface with that ID is registered on the Vue integration all of the requests for that surface are served, in the order they originally arrived.

Specifying surface ID

In the example above, we did not tell JsPlumb what Surface we wanted, so JsPlumb just assumes we want a surface registered with the default id. But if we have more than one surface we can target them specifically by providing the surface ID as the first argument to loadSurface:


import { loadSurface } from '@jsplumbtoolkit/browser-ui-vue3';

let toolkit

export default defineComponent({

mounted() {
toolkit = this.$refs.toolkitComponent.toolkit
loadSurface("myOtherSurface", (s) => {
// s is of type Surface.
})
}
})

MiniviewComponent

This is a component that provides a miniview that can be attached to some surface.

Example

<MiniviewComponent></MiniviewComponent>

Attributes

  • surface-id ID for the surface widget to which to attach the Miniview. Optional; if you don't provide it, a default will be used.

ControlsComponent

Provides a component that offers a set of controls for some surface. If there is a lasso plugin installed on the given surface this component will show buttons for pan/select mode, otherwise it will not.

Example

<ControlsComponent></ControlsComponent>

Attributes

  • surface-id ID for the surface widget to which to attach the controls. Optional; if you don't provide it, a default will be used.
  • undoRedo Defaults to true. Whether or not to show the undo/redo buttons.
  • zoomToExtents Defaults to true. Whether or not to show the zoom to extents button.
  • orientation "row" or "column". The default is "row".

ExportControlsComponent

Provides a component that offers a set of buttons a user can press to export the contents of some surface as SVG, PNG or JPG. Note that this only works when you are using a shape library in your UI. Canvases whose vertices are rendered as HTML elements will not generate output.

Example

<ExportControlsComponent></ExportControlsComponent>

Attributes

  • surface-id ID for the surface widget to which to attach the controls. Optional; if you don't provide it, a default will be used.
  • showLabel Defaults to true. Whether or not to show a label on the controls.
  • label Optional label to use. Defaults to "Export : ".
  • margins Optional margins to set around the output. Defaults to 0. This is an object in PointXY format.
  • svgOptions Optional SvgExportUIOptions controlling SVG output.
  • imageOptions Optional ImageExportUIOptions controlling image output.
  • allowSvgExport Defaults to true.
  • allowPngExport Defaults to true.
  • allowJpgExport Defaults to true.

SurfaceDrop mixin

This mixin is a wrapper around the Drop Manager, which offers the ability to drop onto edges, nodes and the canvas itself.

Example

This is an example of a component that uses the SurfaceDrop mixin. We show, in the onCanvasDrop method, an example of how this mixin can be used to replace the previous Palette mixin. Note, though, the onEdgeDrop and onDrop methods: these are, respectively, called when an element is dragged on an Edge or a Node/Group.

<template>
<div class="sidebar node-palette">
<div class="sidebar-item" :data-jtk-type="entry.type" title="Drag to add new" v-for="entry in data" :key="entry.type">
<i :class="entry.icon"></i>{{entry.label}}
</div>
</div>
</template>

<script>

import { SurfaceDrop } from '@jsplumbtoolkit/browser-ui-vue3';

export default {
mixins:[ SurfaceDrop ],
data:function() {
return {
data:[
{ icon:"icon-tablet", label:"Question", type:"question" },
{ icon:"icon-eye-open", label:"Action", type:"action" },
{ type:"output", icon:"icon-eye-open", label:"Output" }
]
};
}
}

</script>

Note that this component itself doesn't declare any props, and you are free to provide any template you wish to render the component's data. The underlying DragDrop mixin's props are:

  • surfaceId:string Optional. The ID of the Surface to which to attach the Drop Manager. If not provided, the default will be used.
  • selector:string Required. A CSS3 selector instructing the Toolkit how to identify which elements in the component represent draggable node types.
  • dataGenerator:(el:HTMLElement) => T Optional. A function that can return a data object representing an element which is being dragged. This function is called as soon as an element starts to be dragged.
  • allowDropOnGroup:boolean Optional, defaults to true. If true, then elements can be dropped onto nodes/groups, and in the event that occurs, the onDrop method will be called.
  • allowDropOnCanvas:boolean Optional, defaults to true. When an element is dropped on the canvas whitespace, it is added to the dataset and rendered.
  • allowDropOnEdge:boolean Optional, defaults to true. If true, then elements can be dropped onto edges, and in the event that an element is dropped on an edge, a new node/group is added and inserted between the source and target of the original edge, and the original edge is discarded..
  • typeGenerator:(data:T) => string Optional. A function that can return the correct type for some data object representing an element being dragged. By default the Toolkit will use the type member of the data object.
  • groupIdentifier:(d: T, el: HTMLElement) => boolean Optional. By default, the toolkit looks for a jtk-is-group attribute on an element being dragged. If found, with a value of "true", then the Toolkit assumes a group is being dragged. You can supply your own function to make this decision.

ShapePaletteComponent

The shape library plugin also makes available a jsplumb-shape-palette component, which lets you drag and drop new shapes from a palette:

To use it, you need to have setup the shape library as shown above. Then, in your template:

<ShapePaletteComponent :shape-library="shapeLibrary"
:data-generator="dataGenerator"
initial-set="flowchart"/>

Props

  • surfaceId:string Optional. The ID of the surface to which to attach the palette. If not set a default will be used.
  • shapeLibrary:ShapeLibraryImpl Your shape library
  • dragSize:Size Optional size to set on elements when they are being dragged. If this is omitted, the derived iconSize will be used.
  • iconSize:Size Optional, defaults to 150x100 pixels. This is the size to use for each icon. It will also be used for dragSize if that is not separately specified.
  • fill:string Optional, defaults to "#FFFFFF". Fill color to use for icons.
  • outline:string Optional, defaults to "#000000". Color to use for icon outline.
  • selectAfterDrop:boolean Optional, defaults to true. Instructs the palette to set a newly dropped vertex as the Toolkit's selection.
  • canvasStrokeWidth:number Optional, defaults to 2. The stroke width to use when rendering dropped vertices on the canvas.
  • paletteStrokeWidth:number Optional, defaults to 1. The stroke width to use when rendering icons in the palette.
  • dataGenerator:DataGeneratorFunction Optional. Provide one of these if you wish to be able to set initial data for some icon that is about to be dragged on to the canvas. By default JsPlumb will look for attributes with a data-jtk- prefix and use these as the data. For instance an element with data-jtk-type and data-jtk-label attributes will have a data object with type and label values populated from those attributes' values.
  • initialSet If your shape library has multiple sets but when the palette is first rendered you want to specify a single set to show, set this value.