Drag connected elements from the palette

Hello together,

I would like to create a palette extension to drag a larger process snippet like this: connected-elements-expectation

from the palette to the canvas.

I’ve already started to implement and this is the current state: connected-elements-state

You can find my current code base here: https://github.com/ingorichtsmeier/camunda-modeler-plugin-connected-elements/blob/master/client/bpmn-js-extension/IngoPalette.js

What’s missing so far:

  1. The service tasks should contain an failedRetryTimeCycle.
  2. The attached boundary event should reference an error.
  3. The sequence flows.
  4. Some sequence flows should contain an expression.

From debugging and log outputs I found that the parents of my created moddle elements are undefined. How can I set them?

I could not find any example how to add a sequence flow programmatically. Do you have one hidden somewhere?

Thank you in advance for any hints, Ingo

P.S. My JavaScript skills are not so big, but this video helped a lot to get into the stuff: https://www.youtube.com/watch?v=sav98y4EFzE

Hi Ingo!

To set the parent of an element, doesn’t do

businessObject.parent = parentElement;

the trick?

To create sequence flows, the ElementFactory provides help.

var sequenceFlow = elementFactory.createConnection({
    type: 'bpmn:SequenceFlow',
    source: element1,
    target: element2
});

Edit: Solved in a small session together. Feel free to share your findings :+1:

Hi,

after our short session on friday I was able to answer the most of my questions:

Add a failedJobRetryTimeCycle with moddle.create() and add this as an array to the extension element, Finally, add the extension element to the businessObject:

var failedJobRetryTmeCycle = moddle.create('camunda:FailedJobRetryTimeCycle', {
  body: 'R3/PT10S'
});

var r3pt10sExtensionElement = moddle.create('bpmn:ExtensionElements', {
  values: [ failedJobRetryTmeCycle ]
});
helloServicetaskShape.businessObject.extensionElements = r3pt10sExtensionElement;

Just add them with the waypionts:

function createConnection(sourceShape, targetShape, waypoints) {
  return elementFactory.createConnection({type: 'bpmn:SequenceFlow', 
    source: sourceShape, 
    target: targetShape, 
    waypoints: waypoints
  });
}

const sequenceFlowExclusiveNext = 
  createConnection(exclusiveGatewayShape, nextThingServiceTaskShape, [{x:200, y:40}, {x:250, y:40}]);
sequenceFlowExclusiveNext.businessObject.name = 'yes';

Create a formalExpression from the moddle and add it to the businessObject of the sequence flow:

sequenceFlowExclusiveNext.businessObject.conditionExpression = 
   moddle.create('bpmn:FormalExpression', {body: '${continue}'});

It seems, that the parents of the objects are not relevant. Maybe you can clarify this.

What is still missing is the error event. In my example repo (https://github.com/ingorichtsmeier/camunda-modeler-plugin-connected-elements/blob/master/client/bpmn-js-extension/ConnectedElementsPalette.js), the error is shown correctly in the diagram, but it’s missing in the XML.

How should I create an error and reference it in an attached boundary event?

Thank you, Ingo

You created the Error correctly

var error = moddle.create('bpmn:Error', {errorCode: 'abc', name: 'myErrorName'});

But you have to add it to the diagram as rootElement to have it available

var collectionAdd = require('diagram-js/lib/util/Collections').add;

var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject;

var rootElement = canvas.getRootElement(),
    definitions = getBusinessObject(rootElement).$parent,
    rootElements = definitions.get('rootElements');

collectionAdd(rootElements, error);

Furthermore, you should use bpmnFactory instead of moddle to create bpmn:Error and bpmn:ErrorEventDefinition thus ensuring they will have unique IDs.

Here’s a full example: https://codesandbox.io/s/bpmn-js-error-boundary-event-l8ls6

Hi @Niklas_Kiefer, @philippfromme,

thank you for your suggestions and examples, they solved my problems.

For everybody else: here is the solution to create the diagram:

    function createTaskCollection(event) {
      // extension element for the retry time cycle
      var failedJobRetryTmeCycle = bpmnFactory.create('camunda:FailedJobRetryTimeCycle', {
        body: 'R3/PT10S'
      });
        
      var r3pt10sExtensionElement = bpmnFactory.create('bpmn:ExtensionElements', {
        values: [ failedJobRetryTmeCycle ]
      });

      const invokeMyServicetaskShape = elementFactory.createShape({ type: 'bpmn:ServiceTask', x:0, y:0 });
      serviceTaskConfiguration(invokeMyServicetaskShape.businessObject, 'Invoke my service', '${logger}');
      invokeMyServicetaskShape.businessObject.extensionElements = r3pt10sExtensionElement;
      //console.log('the task', helloServicetaskShape);

      const exclusiveGatewayShape = elementFactory.createShape({type:'bpmn:ExclusiveGateway', x:150, y:15 });
      exclusiveGatewayShape.businessObject.name = 'continue?';

      const nextThingServiceTaskShape = elementFactory.createShape({ type: 'bpmn:ServiceTask', x:250, y:0 });
      serviceTaskConfiguration(nextThingServiceTaskShape.businessObject, 'Invoke the next service', '${logger}');
      nextThingServiceTaskShape.businessObject.extensionElements = r3pt10sExtensionElement;

      const otherThingServiceTaskShape = elementFactory.createShape({ type: 'bpmn:ServiceTask', x:250, y:130 });
      serviceTaskConfiguration(otherThingServiceTaskShape.businessObject, 'Do something else', '${logger}');
      otherThingServiceTaskShape.businessObject.extensionElements = r3pt10sExtensionElement;

      const correctItServiceTaskShape = elementFactory.createShape({ type: 'bpmn:ServiceTask', x:120, y:210 });
      serviceTaskConfiguration(correctItServiceTaskShape.businessObject, 'Correct the error', '${logger}');
      correctItServiceTaskShape.businessObject.extensionElements = r3pt10sExtensionElement;
      
      var definitions = bpmnJs.getDefinitions();
      var error = bpmnFactory.create('bpmn:Error', {errorCode: 'abc', name: 'myErrorName'});
      definitions.get('rootElements').push(error);
      
      // error event definition
      var erroreventDefinition = bpmnFactory.create('bpmn:ErrorEventDefinition', {
        errorCodeVariable: 'errorCode',
        errorMessageVariable: 'errorMessage',
        errorRef: error
      });    
      //console.log('errorEventDefinition:', erroreventDefinition);
      
      // attached boundary error event
      const erroreventShape = elementFactory.createShape({ type: 'bpmn:BoundaryEvent', x:50, y:62 });
      erroreventShape.businessObject.name = 'hallo error';
      erroreventShape.businessObject.attachedToRef = invokeMyServicetaskShape.businessObject;
      erroreventShape.businessObject.eventDefinitions = [erroreventDefinition];
      erroreventShape.host = invokeMyServicetaskShape;
      //console.log('the event', erroreventShape);

      erroreventDefinition.$parent = erroreventShape.businessObject;

      const sequenceFlowMyServiceExclusive = 
        createConnection(invokeMyServicetaskShape, exclusiveGatewayShape, [{x:100, y:40}, {x:150, y:40}]);
      
      // need a FormularExpression
      const sequenceFlowExclusiveNext = 
        createConnection(exclusiveGatewayShape, nextThingServiceTaskShape, [{x:200, y:40}, {x:250, y:40}]);
      sequenceFlowExclusiveNext.businessObject.name = 'yes';
      sequenceFlowExclusiveNext.businessObject.conditionExpression = 
        bpmnFactory.create('bpmn:FormalExpression', {body: '${continue}'});
      //console.log('conditional sequence flow: ', sequenceFlowExclusiveNext);

      const sequenceFlowExclusiveOther = 
        createConnection(exclusiveGatewayShape, otherThingServiceTaskShape, [{x:175, y:60}, {x:175, y:170}, {x:250, y:170}]);
      sequenceFlowExclusiveOther.businessObject.name = 'no';
      sequenceFlowExclusiveOther.businessObject.conditionExpression = 
        bpmnFactory.create('bpmn:FormalExpression', {body: '${not continue}'});

      const sequenceFlowErrorCorrect = 
        createConnection(erroreventShape, correctItServiceTaskShape, [{x:68, y:98}, {x:68, y:250}, {x:120, y:250}]);

      const sequenceFlowCorrectHello = 
        createConnection(correctItServiceTaskShape, invokeMyServicetaskShape, 
          [{x:220, y:250}, {x:250, y:250}, {x:250, y: 320}, {x:-30, y:320}, {x:-30, y:50}, {x:0, y:50}]);

      create.start(event, [
        invokeMyServicetaskShape, 
        erroreventShape, 
        exclusiveGatewayShape, 
        nextThingServiceTaskShape, 
        otherThingServiceTaskShape,
        correctItServiceTaskShape,
        sequenceFlowMyServiceExclusive,
        sequenceFlowExclusiveNext,
        sequenceFlowExclusiveOther,
        sequenceFlowErrorCorrect,
        sequenceFlowCorrectHello
      ]);
    }

Thanks again, Ingo

2 Likes