Customize util functions to render labels for custom elements properly

Hey,
we are extending the bpmn-js editor by a custom metamodel, which worked pretty well with the help of the provided tutorials.
In the next step, our goal is to display the element’s label under or on top of the element. We found a workaround using a CustomRenderer and place a text element under our element using diagram-js's textRenderer.
Anyway, our plan is to provide the same display and behavior as shown by the StartEvent or EndEvent, in which the text is displayed as a separate element. Of course, this can be accomplished by adding

 'superClass': [
      'bpmn:EndEvent'
 ],

but this is not a suitable solution. We found that the UpdateLabelHandler's (lib/features/label-editing/cmd/..) preExecute-function decides whether a label is displayed as a separate element or not using the LabelUtil's (lib/util/..) isLabelExternal-function:

export function isLabelExternal(semantic) {
  return is(semantic, 'bpmn:Event') ||
         is(semantic, 'bpmn:Gateway') ||
         is(semantic, 'bpmn:DataStoreReference') ||
         is(semantic, 'bpmn:DataObjectReference') ||
         is(semantic, 'bpmn:DataInput') ||
         is(semantic, 'bpmn:DataOutput') ||
         is(semantic, 'bpmn:SequenceFlow') ||
         is(semantic, 'bpmn:MessageFlow') ||
         is(semantic, 'bpmn:Group');
}

Is it anyway possible to create a CustomLabelUtil that overwrites this according function in order to add the elements of our custom metamodel? We also tried to add a CustomUpdateLabelHandler, which is then be imported as an additional module, but this did not worked.
Is there any other solution than creating a fork?
Thank you for any help.

Can you give some details why your CustomUpdateLabelHandler is not working? How does your setup looks like?

Basically, I copied the UpdateLabelHandler, adjusted the routes accordingly and imported a CustomLabelUtil from the same directory:

import {
  is
} from 'bpmn-js/lib/util/ModelUtil'
import {
  setLabel,
  getLabel
} from 'bpmn-js/lib/features/label-editing/LabelUtil'

import {
  getExternalLabelMid,
  isLabelExternal,
  hasExternalLabel,
  isLabel
} from './CustomLabelUtil'

in order to have access to my custom isLabelExternal function.
Next, I used the same format as usual for the index.js (located in bpmn/labels/..):

import CustomUpdateLabelHandler from './CustomUpdateLabelHandler'

export default {
  __init__: [ 'customUpdateLabelHandler' ],
  customUpdateLabelHandler: [ 'type', CustomUpdateLabelHandler ]
}

and used it similarly to the CustomRenderer:

import CustomPaletteProvider from './palette'
import CustomRenderer from './renderer'
import CustomUpdateLabelHandler from './labels'
...
additionalModules: [
    CustomRenderer,
    CustomPaletteProvider,
    CustomUpdateLabelHandler
]

Nevertheless, the CustomUpdateLabelHandler is called before the UpdateLabelHandler, which results in the problem that the preExecute-function is called on the UpdateLabelHandler but not on my CustomUpdateLabelHandler.

So since the UpdateLabelHandler is a command, instead of overwriting it, you’d able to create a behavior which hooks into the element.updateLabel event. Refer to the existing behaviors. Inside that, you can hook into the preExecute handler.

var LOW_PRIORITY = 500;
eventBus.on('commandStack.element.updateLabel.preExecute', LOW_PRIORITY, function(context) {
  // do whatever you want
});
1 Like

That’s exactly what I was looking for. I added it right after the instance creation and referred the according functions to my CustomLabelUtil. Thank you very much.

Does a similar solution exist for the BpmnViewer?
The according labels are set in the BpmnImporter's add-function, which uses the isLabelExternal function of the /lib/util/LabelUtil again.