BPMN-JS with Angular2 and WebPack

Hi all,

I’m working on an Angular2 application that embeds BPMN-JS. Currently I have a working proof-of-concept with misc features:

  • uses webpack1, angular2.4, Typescript
  • uses explicit webpack-configs, does not use angular-cli
  • bundles bpmn-resources into a separate bmpn-resource-bundle
  • in typescript-files, “require” needed bpmn-scripts
  • import misc bpmn-less files directly from component-css-files
  • sourcemaps (not working perfectly, but it is more or less possible to debug bpmn-js-code)
  • viewer and modeler (in separate angular-components)
  • bpmn-properties-panel
  • custom properties
  • custom palette

This is very much a work-in-progress (and a huge learning curve for me!). I would like to contribute something back to the community, and also I would like to have feedback, help, discussions.

If anyone is interested in this, let me know (in forum or PM) and I will strip corporate stuff from the code and put it up on github.

1 Like

And of course, if anyone has similar code with Angular2 and/or webpack that they want to share/discuss/show off… please do so :slight_smile:

Putting it up as an example without the corporate stuff would be very helpful. I know by hearing that a lot of people are building Angular2 applications :wink:.

I would like to have a look at that. If you could put it on GitHub it would be awesome, @narve.

We are working on a solution to this problem and it looks like we need the same things you accomplished with your code.

Thanks!

Apparently we are looking to build quite the same angular component.
We want to implement a bpmn viewer/navigaror which communicates with a grid.
Would love to coorperate on this topic.

Do you have an ts class, which describes a bpmn-object which should be parsed from a bpmn xml/json.

Thanks you!

The code is now available here: https://github.com/narve/ang2-bpmnjs

Like it says in the README, feedback is welcome. I expect there to be lots of things that could have been done better / shorter / more elegant / more generic / more performant etc.

Hopefully this can become an unofficial blueprint for these kind of projects, with people contributing ideas/improvements as they discover them.

3 Likes

My needs are probably a bit different. I currently don’t have a ts class describing a bpmn-object. On the contrary, my use-case is editing XML and sending the XML (or a JSON with some meta-data and the XML) to a server for storage.

So, I see lots of people have viewed the repository (coming from this forum), and some have cloned the repo, but I didn’t get any comments :slight_smile:

Any feedback on the code, anyone? Things I am wondering on is (e.g.) how to better integrate the PaletteProvider and the PropertiesProvider with the Angular2-component, and how to improve the build so that I have better development workflow (Currently stacktrace in browser usually doesn’t show the original typescript code). All comments, including apparently unhelpful stuff like ‘well done’, ‘this sucks’ or ‘struggling with the same issues, no idea how to help you’) are welcome!

It does not compile on linux because ‘modeler’ should be ‘Modeler’. That’s as far as I’ve gotten.

ERROR in ./src/bpmn.ts
Module not found: Error: Cannot resolve module ‘bpmn-js/lib/modeler’ in /home/ktm/src/ang2-bpmnjs/src
@ ./src/bpmn.ts 3:0-30

Got it to build and run. But then:
[HPM] Error occurred while trying to proxy request /pv/api from localhost:8080 to http://localhost (ECONNREFUSED) (https://nodejs.org/api/errors.html#errors_common_system_errors)

Yes, that’s because I have configured webpack to proxy API-requests (/pv/api) to my API-server running on localhost:80. So if you don’t run anything on localhost:80 it will fail.

Just delete the proxy-element from webpack-dev.js and re-try. I’ll also fix the repo.

Hello Narve,

From github i downloaded the sample project and i am trying to run it. The application loaded home page , viewer and modeler.
Viewer showing the image file , however when i click on the Modeler it shows the BPMN icons but if i drag and drop the pallete to the page it is not happening. Any suggestion would be really helpful for me.

Thanks for trying it out! There were several issues with the code on Github, some due to external libs having problems and some due to code that was specific to my build system.

I’ve added some commits, you should do a git pull and try again. Some hints:

  • you may have to delete the directory node_modules/@types/jasmin before you run npm install, to ensure you get the correct version
  • If you have further problems, copy text from the developer console as well as including a screenshot

Thank you narve for your quick reply . I will take latest from git now and update you about the result. Also i wanted to take your suggestion : Should we take a chance of Integrating BPMN.IO with Angular2 application. How is your experience , any known challenges you already have. Currently one of my other team is using BPMN.IO in normal web application and it is running fine. now we are upgrading our application to Angular2, before we think of upgrading application we should no the supporting of BPMN.io to Angular2. kindly help.

Integrating BPMN.IO and Angular2 wasn’t that hard. The main problem (for me) was getting the build system right (make webpack include correct modules and so on).

I addition, BPMN.IO is mostly synchronous while Angular2 is highly asynchronous, which does require some rethinking.

So: If you are comfortable with Angular2 and Webpack and BPMN.IO, I don’t see any problem combining them. If all of this is new to you (as it was with me), you might have a hard start :slight_smile:

Hi i followed the steps you have mentioned however unfortunately i am getting the same problems. I am not being able to drag and drop the elements in the main page from the pallete. I am getting the following error in the console.
TypeError: Cannot read property ‘ids’ of undefined
at Object.isIdValid (Utils.js:147)
at Object.validate (IdProps.js:29)
at Object.defaultParameters.validate (ValidationAwareTextInput.js:50)
at PropertiesPanel.validate (PropertiesPanel.js:790)
at PropertiesPanel.js:1009
at arrayEach (arrayEach.js:15)
at createForEach.js:15
at PropertiesPanel.js:968
at arrayEach (arrayEach.js:15)
at createForEach.js:15
at PropertiesPanel.js:962
at arrayEach (arrayEach.js:15)
at createForEach.js:15
at PropertiesPanel._updateActivation (PropertiesPanel.js:955)
at PropertiesPanel.update (PropertiesPanel.js:464)
at PropertiesPanel.js:255
at invokeFunction (EventBus.js:455)
at EventBus._invokeListener (EventBus.js:354)
at EventBus._invokeListeners (EventBus.js:342)
at EventBus.fire (EventBus.js:302)
at Canvas.setRootElement (Canvas.js:409)
at Canvas.getRootElement (Canvas.js:360)
at BpmnSnapping.js:59
at invokeFunction (EventBus.js:455)
at EventBus._invokeListener (EventBus.js:354)
at EventBus._invokeListeners (EventBus.js:342)
at EventBus.fire (EventBus.js:302)
at fire (Dragging.js:154)
at move (Dragging.js:184)
at Dragging.init (Dragging.js:477)
at Create.start (Create.js:183)
at Object.createListener (palette.ts:30)
at Palette.trigger (Palette.js:215)
at HTMLDivElement. (Palette.js:121)
at HTMLDivElement. (index.js:35)
at ZoneDelegate.invokeTask (zone.js:275)
at Object.onInvokeTask (core.umd.js:3971)
at ZoneDelegate.invokeTask (zone.js:274)
at Zone.runTask (zone.js:151)
at HTMLDivElement.ZoneTask.invoke (zone.js:345)
EventBus._invokeListener @ EventBus.js:369
EventBus._invokeListeners @ EventBus.js:342
EventBus.fire @ EventBus.js:302
Canvas.setRootElement @ Canvas.js:409
Canvas.getRootElement @ Canvas.js:360
(anonymous) @ BpmnSnapping.js:59
invokeFunction @ EventBus.js:455
EventBus._invokeListener @ EventBus.js:354
EventBus._invokeListeners @ EventBus.js:342
EventBus.fire @ EventBus.js:302
fire @ Dragging.js:154
move @ Dragging.js:184
init @ Dragging.js:477
Create.start @ Create.js:183
createListener @ palette.ts:30
Palette.trigger @ Palette.js:215
(anonymous) @ Palette.js:121
(anonymous) @ index.js:35
ZoneDelegate.invokeTask @ zone.js:275
onInvokeTask @ core.umd.js:3971
ZoneDelegate.invokeTask @ zone.js:274
Zone.runTask @ zone.js:151
ZoneTask.invoke @ zone.js:345

Hi
i was facing same issue. what i did a work around in modelercomponent.ts , i removed from loadBPMN() method and put inside the ngOnInit(). then the drag and drop worked

ngOnInit() {
this.modeler = new modeler({
additionalModules: [
propertiesPanelModule,
propertiesProviderModule,
customPropertiesProviderModule,
customPaletteModule
],
moddleExtensions: {
ne: CustomModdle
},
container: containerRef,
propertiesPanel: {
parent: propsPanelRef
}
});

    this.store.listDiagrams()
        .subscribe( links => this.urls = links);

         console.log( 'load', this.url, this.store );
    this.store.listDiagrams();
    var canvas = this.modeler.get('canvas');
    this.http.get(this.url)
        .toPromise()
        .then(response => response.text())
        .then(data => this.modeler.importXML(data, this.handleError))
        .then( x => x ? this.handleError(x) : this.postLoad() )
        .catch(this.handleError);
}

loadBPMN() {
   
}

Dear Narve,

I am trying to two things for now.

  1. customize the left side pallete to have our own pallets. Request you to suggest where we can customize it.
  2. Save the xml getting generated after creating a diagram.where this functionality to save it, is there the code to save the xml after the diagram creation is done.

Hi @bikashkumar and @Soham_Datta, thanks for the feedback!

I removed some proprietary code before publishing it and didn’t check the code well enough. Please se updated version: Now modeler works without loading pizza-diagram first.

Hi again,

  1. customizing the pallette: The sample on Github already contains a “palette.ts” with customized entries, including custom icons from font-awesome. What is it you need?

  2. In palette.ts line 54 you can see click: () => console.log( 'Save'), this is where I had my implementation of save before I removed it. So you can see where to hook it in. How to implement it depends on your application.

In my application I use a Redux-like pattern, where the click is simply click: () => sendCommand(CMD_SAVE) which is handled by an observer:

        this.store.commands.filter(a => a.action === CMD_SAVE)
            .flatMap((n: any) => Observable.bindNodeCallback(this.modeler.saveXML.bind(this.modeler, {format: true}))())
            .flatMap((xml: string) => this.store.saveXML(this.currentDef, xml))
            ... 

etc, but it really boils down to this.modeler.saveXML( myCallback ) where myCallback will receive the XML as a string (which I then save to the server using AJAX).