How to invoke action on modeler without user interaction

Hi,

I am working on a feature enabling users to “cooperate” on diagram board. I have the communication between them solved, and I will describe an example I want to achieve.

User A adds new element or move some element on the diagram board.
I have successfully hooked into eventBus “shape.created” or “elements.changed” and I have access to the elements which is being manipulated. I am sending them to the User B and now I need to invoke this action on his board and for example create new element on his board -> sync the boards.

I looked into examples and found something like this.modeler.get("modeling")._commandStack.execute('shape.create', XXX)
But I don’t know what should I pass to the function ( the XXX), or if this is even the right way to do this.

Any hints or examples are warmly welcomed !

Yes, creating elements directly via the CommandStack is one option. You can have a look inside the command implementation if you want to know how to use it.

Besides that I’d propose to use Modeling directly which offers an easier-to-use api on top of that.

Thanks for the link, this looks easier than my solution.

I briefly checked the provided link, not on my computer in this moment.
So I can just pass the Shape object I get from “shape.added” listener hooked to eventBus from one client and execute modeling.createShape method on other clients modelers ?

If you successfully built such communication functionality between different Modelers, then you can do that. Note: the eventBus is always responsible for the one Modeler which it is built in . Don’t know about your communication implementation between different clients, as you mentioned.

So generally you can create any shape you want via Modeling#createShape, how you get that shape information depends on your implementation.

I’ve tried to implement it with modeling#createShape method

    const eventBus = this.modeler.get("eventBus");

    // This is triggered on Client A side
    eventBus.on("shape.added", event => {
   
         // this sends the data to server which broadcasts it to other clients
        client.emit("create_element", event.element);
      
    });

    // This is triggered on Client B side
    client.on("create_element", element => {
      const modeling = this.modeler.get("modeling");

     // not sure if the element object already has all the data that this method requires
    // it's the same object provided by eventBus shape.added, not modified in any way.
      modeling.createShape(element, { x: element.x, y: element.y });
    });

With no luck, getting error

Uncaught TypeError: Cannot assign to read only property 'labels' of object '#<Shape>'
    at assign (<anonymous>)
    at assign (index.esm.js:530)
    at create (index.js:251)
    at ElementFactory.create (ElementFactory.js:43)
    at ElementFactory.createBpmnElement (ElementFactory.js:106)
    at ElementFactory.create (ElementFactory.js:31)
    at Modeling._create (Modeling.js:451)
    at Modeling.createShape (Modeling.js:237)

Any ideas how to fix this and how should I get all the shape information I need ?

You need to serialize this element before sending it through the wire and deserialize it on the other side. Elements can not simply be JSON stringified and parsed. The are complex objects with references to other objects etc.

Is there any example how to do that ?
I mean that the business object is pretty complex and inconsistent structure. Also not sure how to replicate the changes on the other side, can this all be done via elementFactory#createShape method ?

What do you mean by that?

You should never access component internals, prefixed with _. Use existing internal APIs, i.e. exposed via Modeling directly.