Programmatically populate collapsed subprocess


I’d like to make use of the new handling of collapsed subprocesses (Handle collapsed sub-processes · Issue #1443 · bpmn-io/bpmn-js · GitHub). More precisely I want to programmatically populate custom subprocesses similar to Creating custom subProcess as atomic operation, but with the difference that my custom subprocess shall initially be collapsed. When diving into the subprocess, the automatically created content shall be shown in its own diagram.

I do have my custom context provider and palette that are used to create my custom subprocesses. I also managed to include the details of the subprocess by:

modeler.on('element.changed', (event) => {
  const element = event.element;

  if ( element.type == 'bpmn:SubProcess' && needsToBePopulated(element) ) {
    const modeling = modeler.get('modeling');
    const elementFactory = modeler.get('elementFactory');

    const testStartTaskShape = elementFactory.createShape({
      type: 'bpmn:StartEvent',
      id: "subStart_1"
    const testServiceTask1 = elementFactory.createShape({
      type: 'bpmn:ServiceTask',
      implementation: "External",
    modeling.createShape(testStartTaskShape, {x:200, y:250}, element);
    modeling.appendShape(testStartTaskShape, testServiceTask1, {x:testStartTaskShape.x+150, y:250}, element)

The problem that I am facing is that the details of the subprocess are shown in the main diagram and not in the diagram that is shown when diving into the subprocess.

The XML for the BPMN looks correct:

<bpmn2:process id="Process_1" isExecutable="false">
  <bpmn2:subProcess id="ResourceActivity_13gilos" collapsed="true">
    <bpmn2:startEvent id="Event_0hpv5px">
    <bpmn2:serviceTask id="Activity_0pmnnzj">
    <bpmn2:sequenceFlow id="Flow_03gii7i" sourceRef="Event_0hpv5px" targetRef="Activity_0pmnnzj" />

but the XML for the BPMNDI shows the problem:

<bpmndi:BPMNDiagram id="BPMNDiagram_1">
  <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
    <bpmndi:BPMNShape id="ResourceActivity_13gilos_di" bpmnElement="ResourceActivity_13gilos">
      <dc:Bounds x="80" y="180" width="570" height="150" />
    <bpmndi:BPMNEdge id="Flow_03gii7i_di" bpmnElement="Flow_03gii7i">
      <di:waypoint x="218" y="250" />
      <di:waypoint x="282" y="250" />
    <bpmndi:BPMNShape id="Event_0hpv5px_di" bpmnElement="Event_0hpv5px">
      <dc:Bounds x="182" y="232" width="36" height="36" />
    <bpmndi:BPMNShape id="Activity_0pmnnzj_di" bpmnElement="Activity_0pmnnzj">
       <dc:Bounds x="282" y="210" width="100" height="80" />
<bpmndi:BPMNDiagram id="BPMNDiagram_0mcjvqn">
  <bpmndi:BPMNPlane id="BPMNPlane_07j8aep" bpmnElement="ResourceActivity_13gilos" />

The diagram for the subprocess ResourceActivity_13gilos is empty and does not contain the content created.

Any idea how to move the elements into this diagram? Many thanks!

P.S. Unfortunately, I did not understand the comment Creating custom subProcess as atomic operation - #3 by nikku because I didn’t find any hint on how and where to use postExecute or how to use modeling.createElement and modelling.connect. Possibly this prevents me from finding a solution to my problem.

Thanks to the code provided in bpmn-js/CustomUpdater.js at develop · bpmn-io/bpmn-js · GitHub I was able to achieve my goal by creating my own updater:

import inherits from 'inherits';

import { is } from 'bpmn-js/lib/util/ModelUtil';

import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';

function ifNewResourceActivity(fn) {
  return function(event) {
    var context = event.context,
        element = context.shape;
    if (is(element, 'bpmn:SubProcess') && element.businessObject.type == 'Resource') {

 * A handler responsible for creating children to a resource subprocess when this is created
export default function ResourceUpdater(eventBus, modeling, elementFactory) {, eventBus);

  function createResourceChildren(evt) {
    var context = evt.context,
	element = context.shape,
        businessObject = element.businessObject;

    // memorise position and size which are changed when adding children
    var x = element.x,
        y = element.y,
        height = element.height, 
        width = element.width;

    const testStart = elementFactory.createShape({
      type: 'bpmn:StartEvent'  
    const testTask1 = elementFactory.createShape({
      type: 'bpmn:Task',

    modeling.createShape(testStart, {x:0, y:0}, element);
    modeling.appendShape(testStart, testTask1, {x:testStart.x+150, y:0}, element)

    // restore position and size which are changed when adding children
    element.x = x;
    element.y = y;
    element.width = width;
    element.height = height;

  ], ifNewResourceActivity(createResourceChildren));


inherits(ResourceUpdater, CommandInterceptor);

ResourceUpdater.$inject = [ 'eventBus', 'modeling', 'elementFactory' ];

The only strange thing is that I have to restore x, y, width, height because they are modified when adding the child elements. In my opinion this feels strange and shouldn’t be necessary. Is there a flag that I should set when adding the shapes?

From the diagram perspective there is two instances of the sub-process:

  • One representing the collapsed, task like shape
  • One representing the plane the sub-process spans (that contains all elements)

You have to find the plane for a collapsed sub-process and add the elements there:

const planeElement = elementRegistry.get(`${SUB_PROCESS_ID}_plane`);

modeling.createShape(..., planeElement);
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.