What kind of DI framework is the modeler using?

Hey,

after struggling myself for quite a while I decided to ask here on the forums.
I can’t figure out what the dependency injection framework for JS, used by camunda, is exactly. It isn’t https://www.npmjs.com/package/inject-js this one here right?

In addition it seems like you use multiple DI frameworks throughout your different projects, is this correct? Sometimes I saw object.$inject = [ array of strings ] but I also saw injector.invoke and injector.get(stringName, boolean), but also quite amusingly object.$inject = [ 'injector' ]

It also surely can’t be the angular injection API since you don’t use annotated injections and your frontend is built with electron and react.

I would appreciate some hints as to where I can learn more about the DI framework (s) used in the modeler and how to tweak the app with my own dependencies. Also if the framework is documented in your github I’m sorry for asking but it wasn’t really clear to me.

EDIT: After some fiddling around I noticed that you call your injection framework from diagram js, and apparently the DI framework is called didi but I never heard of that framework, and $inject also isn’t mentioned there. GitHub - nikku/didi: Dependency Injection for JavaScript

EDIT 2: I think I found the actual part where injector is received from the method. But I’m still confused as to how to create my own dependencies

var fnDef = function fnDef(fn) {
    var locals = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    if (typeof fn !== 'function') {
      if (isArray(fn)) {
        fn = annotate(fn.slice());
      } else {
        throw new Error('Cannot invoke "' + fn + '". Expected a function!');
      }
    }

    var inject = fn.$inject || parse(fn);
    var dependencies = inject.map(function (dep) {
      if (hasProp(locals, dep)) {
        return locals[dep];
      } else {
        return get(dep);
      }
    });

    return {
      fn: fn,
      dependencies: dependencies
    };
  };

Thanks

Yes, didi is the DI framework we use in all of our projects.

Can you tell me more about what problem you are trying to solve? Do you want to add your own feature to bpmn-js or to the Camunda Modeler?

Hey thanks for the answer,

I’m trying to invoke my custom renderer after manually editing business objects (BO). Right now the renderer is only invoked after interacting with the elements in the modeler but not when I add properties to the BO myself.

This is how I export my renderer as a new module and register it

in ./draw
grafik

import {registerBpmnJSPlugin, registerClientPlugin} from 'camunda-modeler-plugin-helpers';


import DrawingModule from "./draw";
registerBpmnJSPlugin(DrawingModule);

My renderer extends the BpmnRenderer like this


export default function SpecialTaskRenderer(
    config, eventBus, styles,
    pathMap, canvas, textRenderer) {

    BpmnRenderer.call(
        this,
        config, eventBus, styles,
        pathMap, canvas, textRenderer,
        1400
    );

    this.canRender = function (element) {
       /// Business Logic
    };

    this.drawShape = function (parent, shape) {
      /// Business Logic
    };
}

inherits(SpecialTaskRenderer, BpmnRenderer);

SpecialTaskRenderer.prototype.drawBpmnShape = BpmnRenderer.prototype.drawShape;


SpecialTaskRenderer.$inject = [
    'config.bpmnRenderer',
    'eventBus',
    'styles',
    'pathMap',
    'canvas',
    'textRenderer'
];

After some reverse engineering I found that I probably had to hook into GraphicsFactory.prototype.update = function(type, element, gfx) from diagram-js. Is this correct? And how would I invoke the method for all shapes and connections?

EDIT: This is kinda off topic but would it be possible to get the current shape object from a BO?

EDIT2: Maybe to be a more specific about what I’m creating: I’m trying to mark multiple tasks to be collapsible into a single task if they meet a shared condition α. If α is met by multiple tasks that share the same parent Γ then all tasks except one should be grouped into a child of Γ. This child of Γ stores the XML representation of all other children of Γ that got collapsed in order to uncollapse them if needed and restore the original state of Γ and it’s children.

Right now I’m trying to mark all children of Γ that fulfill α.

Cheers

Hey,

I tried firing canvas.viewbox.change and canvas.resized events manually via eventBus.fire("canvas.resized") in hope that it would update the whole tab but unfortunately it did not.

Looking forward to hints on how to query my elements for rendering update.

Best regards

I fixed it myself I think.

You can inject elementRegistry and graphicsFactory to retrieve the shape and gfx elements just from the element.id and query updates for that shape.

Thus I created a method that simply takes the ID of an element that should be re-rendered which looks as follows:

export function updateShape(elementId) {
    let shape = elementRegistry.get(elementId);
    let gfx = elementRegistry.getGraphics(shape);
    graphicsFactory.update('shape', shape, gfx);
}

The correct way to update an elements properties is to use Modeling#updateProperties. Using this method the updated element will be re-rendered. As a general rule, do not use low-level methods like GraphicsFactory#update directly unless you really know what you’re doing. Use Modeling instead. When in doubt, ask. When you’re facing a problem do not start with “What DI framework is bpmn-js using?” as it is not the actual question. Start with the problem you’re trying to solve.

I’m not sure how that would work exactly. Are you refering to collapsed sub-processes?

Hey Philipp,

thank you for the answer and for your insight. I was wondering why my XML file didn’t get updated unless I moved the objects in the modeler, but that might be the reason. I’ll make sure to use Modeling#updateProperties from now on.

As to why I phrased my question like this: Originally I really was wondering what kind of DI framework you guys were using. Reason for this is that I’m developing my business logic as a plugin for the modeler (thanks for the easy extendibility by the way) and the whole module injection process wasn’t really clear to me. But thanks for leading me in the right direction.

Well my real problem right now is like I stated, but I’ll try to make it a bit more understandable and critiquable. In the bottom picture you can see the current state of my plugin. You’ll notice that some things are different from the regular modeler:

  • I use custom linting rules that recognize and highlight tasks that can be collapsed. The idea behind this was that some of my tasks, e.g. Type 1 and Type 2 Tasks share properties that enable them to be run in the same Java Bean of the Camunda Cockpit. To minimize resources used (those bean execution methods may run up to several hours), the tasks should be grouped in the modeling stage by Modelers and sent to the backing beans as a single method call with unified or concatted parameters.
  • Reduced palette and context pad to fit our business context
  • Custom properties panel that is still in the making which should hold our business Parameters that get sent to the backend. The properties panel tabs should also be reduced becuase our modelers don’t need to see all that extra information that can be set in BPMN and Camunda.
  • Linting of transitive dependencies as seen in the second image.

The image below shows a new collapsed task, which was the result of collapsing both aqua tasks from the image above. The resulting transitive flow is marked for deletion by the modeler. Also obviously that diagram isn’t going to run in the cockpit becuase the second parallel gateway join doesn’t receive 3 tokens in total but only 2.

The backing beans should also be added dynamically at runtime so I was wondering if instead of using Expression's, an External task with Runners that handle topics dynamically would be smarter.

But that is kind of off topic in this forum and I might create another thread for that matter later in time when I’m implementing the backend.

Thank you for your patience and I hope to hear back from you
Best regards
N

So where exactly are you stuck at the moment?

I’m trying to store the meta data of the collapsed tasks in the resulting tasks but I don’t really know how to extract everything I need. This would be needed to uncollapse the task again and restore the original two tasks, even after quitting the editor.
I mean ideally I would just store the whole XML representation of the Shapes and Business Objects as a BPMN:Extension XML field.