For plugin: How to get an html element from properties panel

Our customer currently uses an older version of the modeler (4.x.x) with some plugins we wrote. One of them being an autocomplete list for specific data. That autocomplete list is used on some inputs for example expressions and execution listeners.

In 4.x.x it was possible to listen to the “propertiesPanel.changed” event on the eventBus and you got the current diagram element as well as the panel, tabs, groups, entries and so on with connections to the corresponding html elements in which you could also query if needed.

As far as we understand the 5.x.x modeler (Correct me if I’m wrong):

  • There is no “propertiesPanel.changed” event anymore, it seems “propertiesPanel.updated” is kinda the new one?
  • You get the current selected diagram element through this event, but no tabs of course, as there are no tabs anymore in the new modeler.
  • Groups can be accessed through a Custom Property Provider. But the entries of a group have an id, an “isEdited” property and a component, which is kinda useless at least for our purpose.

The autocompleter needs some element to get attached to, so it can render the dropdown list. Is there a way to achieve this? And also especially for the execution listeners list. This behaves really strange if you try to query the html elements with “normal” means like document.querySelector() for example because if they get changed on the propertiesPanel.layoutChanged, they are not there for a moment so the query returns null.

Thanks in advance

Does nobody else have the need to get some html element reference from the existing properties panel? I’m just curious, our case doesn’t seems so unusual.
So I guess there is no way to get some information from the modeler api, but what about the events from the eventbus? Escpecially for the Execution Listeners html input elements. At what point are they rerendered so it would be safe to query them?

Is this autocomplete feature added only to custom entries or also to existing entries?

Hey Philipp,

we only want to put this autocomplete on existing entries, mainly in this case on the input element at Execution Listeners with the Listener Type ‘Expression’.

modeler_execlist

We use the autocompleter library: https://www.npmjs.com/package/autocompleter

Which - to make it short - needs an html element to attach to and some data, to show in a list. Something like

{
        input: htmlInputElement,
        fetch: // data,
        onSelect: (item: Item) => { /* do stuff */ }
}

So we try to query the input element in the listener, which doesn’t really works well, if at all. Especially if you have more than one listener. I try to explain how we try to achieve that, but our solution feels pretty hacky so I was hoping there is a better and more consistent way:

So we register a service and inject the eventBus and in the constructor, we listen for two events:

eventBus.on('propertiesPanel.updated', (current: any, _: any) => this.currentElement = current); // the selected diagram element
eventBus.on('propertiesPanel.layoutChanged', (_: any) => /* query the input element and do autocompleter logic */);

In the “layoutChanged” we try to query for the elements, but this doesn’t always work.

document.querySelectorAll('.bio-properties-panel-collapsible-entry').forEach((element: Element, index: number) => {

            const htmlElement = element as HTMLElement;
            const typeElement = htmlElement.querySelector(`#bio-properties-panel-${current.element.id}-executionListener-${index}-listenerType`) as HTMLSelectElement;

            if (typeElement?.value === 'expression') {

                const inputElement = htmlElement.querySelector(`#bio-properties-panel-${current.element.id}-executionListener-${index}-expression`) as HTMLInputElement; // is sometimes null when the event is fired?

                if (inputElement) {
                    retval.push(inputElement);
                }
            }            
        });

So that’s practically all there is to it. So is there currently in the modeler 5 a better way to get html elements? Or a better way to achieve what we’re trying to do?

The new properties panel is built with Preact, meaning that it’s rendered asynchronously. The propertiesPanel.updated event is fired when the selected element changes. At that point it is not guaranteed that the properties panel has been rendered. I guess the event name is misleading. Generally, the new properties panel isn’t supposed to be extended through mutating the DOM from outside, as this generally isn’t a good idea when working with virtual DOM applications.

On the other hand, there’s currently no way to extend existing entries with features like autocomplete. If you think this is a missing feature, feel free to request it here: GitHub - bpmn-io/bpmn-js-properties-panel: A properties panel for bpmn-js.

In the meantime, you could try to work around this issue by using setTimeout to make sure the properties panel has rendered before adding autocomplete.

eventBus.on('propertiesPanel.updated', () => {
  setTimeout(() => {
    autocomplete({ input: document.querySelector(...), ... });
  }, 0);
});

Hey Philipp, thanks for your reply.

Yeah I thought so, but what can I say? We need to implement it for our customer. I think in this case it’s not that big of a deal as the dropdown is only rendered when nothing else is happening. But anyway, at least it means there is no api to change existing entries.

Can you say something about the propertiesPanel.layoutChanged event? It seemed more suitable as far as I could see, I think it gets fired after everything is rendered?

But we kinda have a solution for our problem for now. Our query for the elements was not correct, as the index from the queried nodeList is not the same as the index the entries are rendered with, so this also kinda works then.

Thanks also for suggesting the feature request. I guess we will talk about it with some other coworkers and come back to it for future plugins/extensions we need to integrate.

So far, have a nice day.

1 Like

propertiesPanel.layoutChanged is fired e.g. when I open or close a group in the properties panel. For your use case propertiesPanel.updated should work (apart from having to use timeouts). In the future I could also imagine having a generic autocomplete feature in the properties panel that you can hook into an provide data for.