We have been using gojs for 3 years with vue 2.
We are now conducting a migration to vue 3 and it seems that gojs does not like any vue 3 objects that contain gojs configuration data.
Even after removing the vue 3 reactivity proxy using the toRaw
helper gojs still throws an error.
Here is an example of our base diagram component which works with vue 2 but only partially with vue 3:
<template>
<div ref="diagram" :class="$style.diagram"></div>
</template>
<script>
import * as go from 'gojs'
import { isEmpty, cloneDeep } from 'lodash'
import { toRaw, markRaw } from 'vue'
let Diagram = null
const $ = go.GraphObject.make
export default {
name: 'Diagram',
props: {
model: {
type: [Object, Array],
default: () => ({
nodes: [],
links: [],
}),
},
options: {
type: Object,
default: () => ({}),
},
linkTemplate: {
type: Array,
default: () => [],
},
groupTemplateMaps: {
type: Object,
default: () => ({}),
},
nodeTemplateMaps: {
type: Object,
default: () => ({}),
},
highlightBin: {
type: [String, Number],
default: '',
},
},
watch: {
model(newval, oldval) {
if (Diagram && Diagram.model) {
Diagram.model.commit(model => this.updateModel(model, newval, oldval))
}
},
highlightBin: {
handler: 'setSegmentedBin',
immediate: true,
},
},
async beforeUnmount() {
await this.$nextTick()
if (Diagram) {
Diagram.div = null
Diagram.clear()
Diagram = null
}
},
created() {
// register gojs license
const { GOJS_LICENSE } = this.$store.state.consts
if (GOJS_LICENSE) go.licenseKey = GOJS_LICENSE
},
async mounted() {
// Wait for vue to update data and DOM
await this.$nextTick()
this.initDiagram()
},
methods: {
getDiagramInstance() {
return Diagram
},
setSegmentedBin(binId) {
if (Diagram) {
Diagram.model.setDataProperty(Diagram.model.modelData, 'segmentedBin', binId)
}
},
initDiagram() {
if (Diagram === null) {
// Init gojs diagram
let diagram = $(go.Diagram, this.$el, this.options)
let vm = this
let templateMap = new go.Map()
let groupTemplateMap = new go.Map()
if (!isEmpty(this.linkTemplate)) diagram.linkTemplate = $(...this.linkTemplate)
// Add nodeTemplateMaps per keys
if (!isEmpty(this.nodeTemplateMaps)) {
for (const [key, value] of Object.entries(toRaw(this.nodeTemplateMaps))) {
if (key === 'default') templateMap.add('', value)
else templateMap.add(key, value)
}
}
// Add groupTemplateMaps per keys
if (!isEmpty(this.groupTemplateMaps)) {
for (const [key, value] of Object.entries(toRaw(this.groupTemplateMaps))) {
if (key === 'default') groupTemplateMap.add('', value)
else groupTemplateMap.add(key, value)
}
}
diagram.nodeTemplateMap = templateMap
diagram.groupTemplateMap = groupTemplateMap
let { model } = vm
let { nodes } = model
let { links } = model
nodes = toRaw(nodes)
links = toRaw(links)
diagram.model = $(go.GraphLinksModel, {
linkKeyProperty: 'key',
linkFromPortIdProperty: 'fromPort',
linkToPortIdProperty: 'toPort',
nodeDataArray: nodes,
linkDataArray: links,
})
Diagram = diagram
this.$emit('diagram-ready', diagram)
}
},
arrayToMap(array) {
let keyMap = {}
for (let asset of array) {
keyMap[asset.key] = asset
}
return keyMap
},
diffMap(origMap, newMap) {
let onlyInOrig = []
let onlyInNew = []
let changed = []
for (let key of Object.keys(origMap)) {
if (!newMap.hasOwnProperty(key)) {
onlyInOrig = onlyInOrig.concat(key)
}
}
for (let key of Object.keys(newMap)) {
if (!origMap.hasOwnProperty(key)) {
onlyInNew = onlyInNew.concat(key)
} else {
if (origMap[key].hasOwnProperty('last_modified') && origMap[key].last_modified != newMap[key].last_modified) {
changed = changed.concat(key)
}
}
}
return {
delete: onlyInOrig,
add: onlyInNew,
changed,
}
},
getAssetFromKeyArray(array, key) {
for (let asset of array) {
if (asset.key == key) {
return asset
}
}
},
diffKeyedArray(origArr, newArr) {
let newMap = this.arrayToMap(newArr)
let oldMap = this.arrayToMap(origArr)
let diff = this.diffMap(oldMap, newMap)
return diff
},
updateModel(model, newval, oldval) {
if (Diagram) {
let nodesDiff = this.diffKeyedArray(oldval.nodes, newval.nodes)
for (let key of nodesDiff.delete) {
model.removeNodeData(model.findNodeDataForKey(key))
}
for (let key of nodesDiff.add) {
model.addNodeData(this.getAssetFromKeyArray(newval.nodes, key))
}
for (let key of nodesDiff.changed) {
model.removeNodeData(model.findNodeDataForKey(key))
model.addNodeData(this.getAssetFromKeyArray(newval.nodes, key))
}
let linksDiff = this.diffKeyedArray(oldval.links, newval.links)
for (let key of linksDiff.delete) {
model.removeLinkData(model.findLinkDataForKey(key))
}
for (let key of linksDiff.add) {
model.addLinkData(this.getAssetFromKeyArray(newval.links, key))
}
}
},
},
}
</script>
<style lang="scss" module>
.diagram {
width: 100%;
height: 100%;
canvas {
outline: none;
}
}
</style>
When passing in these nodes:
[{"key":"GROUP|types","isGroup":true,"title":"Types","__gohashid":583},{"key":"GROUP|services","isGroup":true,"title":"Services","__gohashid":584},{"key":"C_ARM|Philips","category":"block_node_template","icon":"/s3/statics/customer-dashboard-ui/assets/images/asset-icons/C_ARM.svg","device_type":"C_ARM","type_display_name":"C-Arm","vendor":"Philips","count":4,"isSegmented":false,"segmentedId":null,"group":"GROUP|types","__gohashid":585},{"key":"Cloud Services/Clinical Analytics","category":"service_template","label":"Cloud Services Clinical Analytics","serviceName":"Cloud Services/Clinical Analytics","group":"GROUP|services","__gohashid":586}]
And this link data:
[{"from":"C_ARM|Philips","to":"Cloud Services/Clinical Analytics","key":"C_ARM|Philips|Cloud Services/Clinical Analytics","direction":"outbound","__gohashid":588}]
I get the following error in the console:
Uncaught Error: EnumValue.Default is not a valid geometryStretch.
at B (go.js?0187:formatted:137)
at Kf.t.Hm (go.js?0187:formatted:29019)
at Kf.Y.measure (go.js?0187:formatted:20905)
at Qm.measure (go.js?0187:formatted:25574)
at S.t.Hm (go.js?0187:formatted:26538)
at S.Y.measure (go.js?0187:formatted:20905)
at R.t.Vw (go.js?0187:formatted:15105)
at th (go.js?0187:formatted:15087)
at Hf (go.js?0187:formatted:15030)
at R.t.hd (go.js?0187:formatted:14995)
Is there a way to fix this?