Hi,
I am integrating BPMN-js in one of my Angular2+ projects. I am done with most of the requirements and currently, everything is working fine. The only remaining requirement is, I want to remove some of the elements from the Palette as well as from the Context Menu. I have found discussions on these topics here.
It seems like I need to override the default getPaletteEntries
and getContextPadEntries
by deleting the required entries. I have tried the same, however, nothing seems to work. I am sure that I am doing something wrong or maybe putting the code somewhere at the wrong place.
Following is the code of my bpmn service where I am managing most of the code related to bpmn-js integration.
import {
ElementRef,
Injectable,
Renderer2,
RendererFactory2,
} from '@angular/core';
import * as BpmnModeler from 'bpmn-js/dist/bpmn-modeler.production.min.js';
import * as BpmnViewer from 'bpmn-js/dist/bpmn-viewer.production.min.js';
import { Subject } from 'rxjs';
import { BpmnConstantsService } from './bpmn-constants.service';
import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider.js';
import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider.js';
var _contextPadEntries = ContextPadProvider.prototype.getContextPadEntries;
var _paletteEntries = PaletteProvider.prototype.getPaletteEntries;
/* Code not working */
ContextPadProvider.prototype.getContextPadEntries = function (element) {
const entries = _contextPadEntries.apply(this);
delete entries['append.end-event'];
return entries;
};
/* Code not working */
PaletteProvider.prototype.getPaletteEntries = function (element) {
const entries = _paletteEntries.apply(this);
delete entries['create.exclusive-gateway'];
delete entries['create.intermediate-event'];
delete entries['create.task'];
delete entries['create.data-store'];
return entries;
};
@Injectable({
providedIn: 'root',
})
export class BpmnService {
private _bpmnModeler: any;
private _bpmnViewer: any;
private _renderer: Renderer2 = null;
private _bpmnInstance: any;
public eventOutput: Subject<null | string> = new Subject<null>();
constructor(private _rendererFactory: RendererFactory2) {
this._bpmnModeler = new BpmnModeler({
keyboard: { bindTo: document },
});
this._bpmnViewer = new BpmnViewer({ keyboard: { bindTo: document } });
this._renderer = _rendererFactory.createRenderer(null, null);
}
public async createNewDiagram(
xml: string,
el: ElementRef,
editableMode: boolean = true
): Promise<void> {
const diagramXML = xml ? xml : BpmnConstantsService.NEW_DIAGRAM_XML;
await this.openDiagram(diagramXML, el, editableMode);
}
public async openDiagram(
xml: string,
el: ElementRef,
editableMode: boolean = true
): Promise<void> {
try {
if (editableMode) {
this._bpmnInstance = this._bpmnModeler;
} else {
this._bpmnInstance = this._bpmnViewer;
}
await this._bpmnInstance.importXML(xml);
this._bpmnInstance.get('canvas').zoom('fit-viewport', 'auto');
this._renderer.addClass(el.nativeElement, 'with-diagram');
this._renderer.removeClass(el.nativeElement, 'with-error');
var eventBus = this._bpmnInstance.get('eventBus');
eventBus.on('element.dblclick', (e) => {
this.eventOutput.next(e.element.id);
});
} catch (err) {
this._renderer.removeClass(el.nativeElement, 'with-diagram');
this._renderer.addClass(el.nativeElement, 'with-error');
}
}
public attachModelerToCanvas(el: ElementRef): void {
this._bpmnInstance.attachTo(el.nativeElement);
}
public closeDiagram() {
this._bpmnInstance.destroy();
}
public async exportDiagram(): Promise<string> {
const result = await this._bpmnInstance.saveXML();
const { xml } = result;
return xml;
}
public zoomController(step: number, resetZoom: boolean = false) {
if (resetZoom) {
this._bpmnInstance.get('canvas').zoom('fit-viewport', 'auto');
return;
}
this._bpmnInstance.get('zoomScroll').stepZoom(step);
}
public toggleFullScreenView(el: ElementRef) {
if (!document.fullscreenElement) {
el.nativeElement.requestFullscreen();
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
}
}
App component HTML where I am integrating the HTML part,
<div class="content" #jsDropZone>
<div class="message intro">
<div class="note">
Drop BPMN diagram from your desktop or <span (click)="create()">create a new diagram</span> to get
started.
</div>
</div>
<div class="message error">
<div class="note">
<p>Ooops, we could not display the BPMN 2.0 diagram.</p>
</div>
</div>
<div class="canvas" #canvas></div>
<div class="io-zoom-control">
<ul class="io-control-list">
<li><button mat-icon-button (click)="zoomIn()"><mat-icon>zoom_in</mat-icon></button></li>
<li><button mat-icon-button (click)="zoomOut()"><mat-icon>zoom_out</mat-icon></button></li>
<li><button mat-icon-button (click)="resetZoom()"><mat-icon>crop_free</mat-icon></button></li>
<li><button mat-icon-button (click)="fullScreen()">
<mat-icon *ngIf="!isFullScreenViewActive">zoom_out_map</mat-icon>
<mat-icon *ngIf="isFullScreenViewActive">zoom_in_map</mat-icon>
</button></li>
</ul>
</div>
</div>
<button (click)="save()">SAVE</button>
App component TS file where I am accessing the service
import { Component, ElementRef, ViewChild } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { BpmnConstantsService } from './bpmn-constants.service';
import { BpmnService } from './bpmn.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent {
@ViewChild('jsDropZone', { static: true }) private el: ElementRef;
@ViewChild('canvas', { static: true }) private canvas: ElementRef;
public isFullScreenViewActive: boolean = false;
constructor(public bpmnService: BpmnService, private _snackbar: MatSnackBar) {}
create() {
this.bpmnService.createNewDiagram(
BpmnConstantsService.NEW_DIAGRAM_XML,
this.el
);
}
showSnackBar(message: string) {
this._snackbar.open(message, 'Ok', { duration: 2000 });
}
async ngOnInit() {
const diagram = localStorage.getItem('diagram');
await this.bpmnService.createNewDiagram(diagram, this.el, true);
this.bpmnService?.eventOutput?.subscribe(res => {
this.showSnackBar(`Clicked element 👉 ${res}`)
})
}
ngAfterContentInit() {
this.bpmnService.attachModelerToCanvas(this.canvas);
}
async save() {
const xml = await this.bpmnService.exportDiagram();
localStorage.setItem('diagram', xml);
}
zoomIn() {
this.bpmnService.zoomController(1);
}
zoomOut() {
this.bpmnService.zoomController(-1);
}
resetZoom() {
this.bpmnService.zoomController(0, true)
}
fullScreen() {
this.isFullScreenViewActive = !this.isFullScreenViewActive;
this.bpmnService.toggleFullScreenView(this.el);
}
}
Can someone help me with this integration part? Any kind of help is appreciable.