Bpmn js and Angular JS 1.5


#1

Hello everyone,
Is there an implementation of bpmn-js in Angularjs 1.5.

Thanks in advance.
Kind regards


#2

Hi,

Do not think there is one, unfortunately.

There is, however, an Chrome app example with an example of how to do that (can not judge how good it is, though):
repository
link to relevant file

Cheers,
Vladimir


Angularjs directive for bpmn modeler
#3

Hi,

I have an angular 1.5 implementation in this repository (instructions included). It’s based on an old version of BPMN-JS but it can help you as a starting guide.

Relevant files:
Angular app
Modeler
Viewer

Regards,
David


#4

Hello David,
Thank you so much, I will check your implementation and let you know.

Kind regards,

Dhaker


#5

Hello David,

I can’t figure out how i can inject and instanciate my custom modeler in order to import the bpmn 2.0 xml
below the followed steps:

thank you in advance.

in the app.js:

    require('bpmn-js/lib/Modeler');
    require('./custom-modeler');
    global.jQuery = require('jquery');
    require('jquery-ui-dist/jquery-ui.js');	
    require('bootstrap');
    require('angular/angular');
    require('angular-route/angular-route');
    require('angular-ui-bootstrap');

    angular.module('app', ['ui.bootstrap', require('bpmn-js/lib/Modeler')]);
    require('./directives/myFlow.directive');

in myFlowDirective.js:

(function () {
	'use strict';
	angular.module('app').directive('myFlow', enformFlow);
	enformFlow.$inject = ['$window', 'CustomModeler'];

	function enformFlow($window, CustomModeler) {
		// Usage:
		//     <my-flow></my-flow>
		var directive = {
			link: link
			, restrict: 'EA'
			, replace: true
			, templateUrl: './templates/myFlow.template.html'
			, height: '='
			, width: '='
			, title: '@'
		}
		return directive;

		function link(scope, element, attrs) {
			var modeler = new CustomModeler({
				container: 'wrapper'
				, keyboard: {
					bindTo: document
				}
			});
			modeler.importXML(bpmnXML, function (err) {
				if (err) {
					console.error('something went wrong:', err);
				}
				modeler.get('diagram').zoom('fit-viewport');
				modeler.addCustomElements(customElements);
			});
			// expose bpmnjs to window for debugging purposes
			window.bpmnjs = modeler;
		}
	}
})();

error message:

angular.js:68 Uncaught Error: [$injector:modulerr] Failed to instantiate module app due to:
Error: [$injector:modulerr] Failed to instantiate module function CustomModeler(options) due to:
Error: [$injector:unpr] Unknown provider: options
http://errors.angularjs.org/1.5.8/$injector/unpr?p0=options
at http://localhost:9013/app.js:9842:12
at http://localhost:9013/app.js:14285:19
at getService (http://localhost:9013/app.js:14438:39)
at injectionArgs (http://localhost:9013/app.js:14462:58)
at Object.invoke (http://localhost:9013/app.js:14484:18)
at http://localhost:9013/app.js:14396:45
at forEach (http://localhost:9013/app.js:10095:20)
at loadModules (http://localhost:9013/app.js:14375:5)
at http://localhost:9013/app.js:14392:40
at forEach (http://localhost:9013/app.js:10095:20)
http://errors.angularjs.org/1.5.8/$injector/modulerr?p0=function%20CustomMo…0%20%20at%20forEach%20(http%3A%2F%2Flocalhost%3A9013%2Fapp.js%3A10095%3A20)
at http://localhost:9013/app.js:9842:12
at http://localhost:9013/app.js:14414:15
at forEach (http://localhost:9013/app.js:10095:20)
at loadModules (http://localhost:9013/app.js:14375:5)
at http://localhost:9013/app.js:14392:40
at forEach (http://localhost:9013/app.js:10095:20)
at loadModules (http://localhost:9013/app.js:14375:5)
at createInjector (http://localhost:9013/app.js:14297:19)
at doBootstrap (http://localhost:9013/app.js:11532:20)
at bootstrap (http://localhost:9013/app.js:11553:12)


#6

Hi Dhaker,

You can include the Modeler as a constant, like here. For you case,

var customModeler = require('./custom-modeler'); // for readability
// (...)
angular
    .module('app', ['ui.bootstrap'])
    .constant('CustomModeler', customModeler);
require('./directives/myFlow.directive');

Regards.


#7

Kudos to you David, you save my day :slight_smile:

Best regards,


#8

David,
just for clarification, in your example are you using the window object to inject your constants so they could be globally accessed?

(function() {
  "use strict";
  angular
    .module('custom-bpmnjs', [
      // Angular modules
      'ngAnimate',
      'ngAria',
      'ngMessages',
      // 3rd Party Modules
      'ui.router',
      'ui.bootstrap',
      'ngStorage',
      'pascalprecht.translate',
      'toaster'
    ])
    .constant('Modeler', window.BpmnJSCustom.Modeler)
    .constant('Viewer', window.BpmnJSCustom.Viewer)
    .constant('PropertiesProviders', window.BpmnJSCustom.PropertiesProviders);
})();

#9

Hi Dhaker,

Yes, they’re already globally available. Indeed, you could access to them with $window, e.g.

(function () {
    'use strict';
    angular.module('app').directive('myFlow', enformFlow);
    enformFlow.$inject = ['$window'];

    function enformFlow($window) {
        // even window.CustomModeler works
        var CustomModeler = $window.CustomModeler; 
	// Usage:
	//     <my-flow></my-flow>
	var directive = {
		link: link

But, these ways are considered bad practices


#10

Hello David,
Thank you again, Injecting vendor library thru constant is working well and it’s a good workaround, however, i’m facing an issue below a screenshoot:
What i am trying to perform is:

  1. integrating the bpmn js custom viewer within an Angular JS directives,
  2. Include the directive to a popup based on bootstrap modal

Any clue to resolve this issue :slight_smile:

(function () {
	'use strict';
	angular.module('app').directive('myFlow', myFlow);
	myFlow.$inject = ['$window', 'CustomModeler'];

	function myFlow($window, CustomModeler) {
		// Usage:
		//     <my-flow></my-flow>
		var directive = {
			link: link
			, restrict: 'EA'
			, replace: true
			, template: '<div id="wrapper1" title="My Flow Editor"></div>'
			, height: '='
			, width: '='
			, title: '@'
		}
		return directive;

		function link(scope, element, attrs) {
			var modeler = new CustomModeler({
				container: 'wrapper1'
				, keyboard: {
					bindTo: document
				}
			});
			modeler.importXML(bpmnXML, function (err) {
				if (err) {
					console.error('something went wrong:', err);
				}
				modeler.get('wrapper1').zoom('fit-viewport');
				modeler.addCustomElements(customElements);
			});
			// expose bpmnjs to window for debugging purposes
			window.bpmnjs = modeler;
		}
	}
})();

Best regards,

Error message:

VM390:1 POST http://fake.com/ net::ERR_BLOCKED_BY_CLIENT(anonymous function) @ VM390:1(anonymous function) @ VM390:1
angular.js:13920 TypeError: Cannot read property ‘appendChild’ of null
at CustomModeler.Viewer._createContainer (http://localhost:9013/app.js:44591:9)
at CustomModeler.Viewer (http://localhost:9013/app.js:44287:25)
at CustomModeler.Modeler (http://localhost:9013/app.js:44041:10)
at new CustomModeler (http://localhost:9013/app.js:991:11)
at Object.link (http://localhost:9013/app.js:1132:18)
at http://localhost:9013/app.js:11028:18
at invokeLinkFn (http://localhost:9013/app.js:19715:9)
at nodeLinkFn (http://localhost:9013/app.js:19116:11)
at http://localhost:9013/app.js:19454:13
at processQueue (http://localhost:9013/app.js:26164:28)


#11

Sure, man.

You need to wait the DOM to render. Use $timeout.

(function () {
	'use strict';
	angular.module('app').directive('myFlow', myFlow);
	myFlow.$inject = ['$window', '$timeout', 'CustomModeler'];

	function myFlow($window, $timeout, CustomModeler) {
		// Usage:
		//     <my-flow></my-flow>
		var directive = {
			link: link
			, restrict: 'EA'
			, replace: true
			, template: '<div id="wrapper1" title="My Flow Editor"></div>'
			, height: '='
			, width: '='
			, title: '@'
		}
		return directive;

		function renderModeler(){
			var modeler = new CustomModeler({
				container: 'wrapper1'
				, keyboard: {
					bindTo: document
				}
			});
			modeler.importXML(bpmnXML, function (err) {
				if (err) {
					console.error('something went wrong:', err);
				}
				modeler.get('wrapper1').zoom('fit-viewport');
				modeler.addCustomElements(customElements);
			});
			// expose bpmnjs to window for debugging purposes
			window.bpmnjs = modeler;
		}

		function link(scope, element, attrs) {
			var that = this;
			$timeout(function(){
				renderModeler.call(that);
			}, 0);
		}
	}
})();

I didn’t test it, but it’s the idea of how to use the $timeout function.


#12

Hello David,
Thank you, I will try that :slight_smile:


#13

Hello David,
Unfortunately , the same error persist, although the DOM has finished rendring, but the exception occurs in
http://localhost:9013/node_modules/bpmn-js/lib/Viewer.js class,

  1. Initially the parent object was set to the wrapper1 ( template: '<div id="wrapper1" title="My Flow Editor" </div>')

  1. In the next line, the parent object is flushed after being passed to the domQuery

    Viewer.prototype._createContainer = function(options) {

    var parent = options.container,
    container;

    // support jquery element
    // unwrap it if passed
    if (parent.get) {
    parent = parent.get(0);
    }

    // support selector
    if (isString(parent)) {
    parent = domQuery(parent);
    }

    container = domify(’

    ’);

    assign(container.style, {
    width: ensureUnit(options.width),
    height: ensureUnit(options.height),
    position: options.position
    });

    parent.appendChild(container);

    return container;
    };

Do you have any clue?

Best regards,


#14

Hi Dhaker,

That occurs because,

var modeler = new CustomModeler({
			container: 'wrapper1'

should be,

var modeler = new CustomModeler({
			container: '#wrapper1'

domQuery works with jQuery selectors syntax.

Regards


#15

Hi David,

That make sense, but a new error message occurs (check below screenshoot):

thanks a ton friend :smiley:.


#16

:confused:

This seems to be a problem of your custom modeler more than Angular. Have you tested it in a non-angular environment? Can you share a larger fragment of your code?


#17

Hello Bpmn js Team and David,
Thank you so much, You always point me to the right direction to solve my issues.

I’m hardly trying to display within a bootstrap modal popup the bpmn js diagram which is wrapped within a n Angular JS directive:

  1. the red region (div id: palette) will hold the palette,
  2. the gray region (div id: diagram) will contain the diagram,

but as you can see the layout is messed up :grin::
Below link for the code source, can you please take a look,any guidance or recommandation to perform this implmentation are welcomed.

https://drive.google.com/file/d/0B5ruioHjOasTc0s4VXM4UTdBZTg/view?usp=sharing

Thank you again.


#18

Hi Dhaker,

Without doing a deep review, I’d suggest changing in the file ./app/directives/enformFlow.directive.js (L:45-47)

var modeler = new CustomModeler({
			//container: '#wrapper'
			container: targetElement[0] // here

to

var modeler = new CustomModeler({
			//container: '#wrapper'
			container: targetElement[0].querySelector("#diagram")

#19

Thank you very much David, i already set it as below and it’s working like a charm :smile:

var modeler = new CustomModeler({
			 container: "#diagram'

Have you any clue how to move the palette to the red region (div id: palette) (as mentioned in this post by Vladimirs i need to override the init method )

kudos to you team, i’really appreciate your collaboration and dedication.


#20

Hi dhaker,

I see that this topic was closed some time ago. Did you finish the implementation? It would be very useful if I could have access to the final code. I’m working on a similar project.
:slight_smile: