Dynamic forms with Camunda BPM

4,508 views
Skip to first unread message

Fer

unread,
Feb 24, 2014, 8:56:48 AM2/24/14
to camunda-...@googlegroups.com
Hello,

First of all, thanks everybody for your help. I'm new to JEE, Spring and Camunda, so I apologize for my newbie questions.

What I'm trying to do right now is to have a dynamic form. For example, I want to retrieve from my database an inventory (list of assets on sale) and show it in a User Task through a embedded HTML form. I would like to show a table where we could see per line the asset name in the first column, in the second one the stock and in the third one a text input to choose a value.

I really have no idea about how to do a loop with the data. In the first place, because we can't have complex data types (if you remember, we talked about using a XML/JSON as a workaround) and I don't know how to store a collection without using a JSON as a string. Secondly, because I do not know how to have a loop and create a row per item. If I knew the number of items in advance I could create variables such as "assetName_i", where i is the item's position, but it's completely static in the HTML form (you have to know it in advance, if I'm not wrong).

Another workaround/idea I have to get over this is to load a basic HTML form as embedded, and afterwards getting all the info with Javascript API and creating dynamically the rows by using jQuery. It seems a little bit too complex to do what we want to do, but I don't know a better option so far (I guess there is though). And even though, with this JS workaround we still would have problems with the execution variables after submitting the form (I think).

I've searched Google and these forums, and again I did not find any related topic. If you could just tell me what to look for or an idea to research, I'd be really thankful.


Thanks for your time.

webcyberrob

unread,
Feb 24, 2014, 9:58:01 PM2/24/14
to camunda-...@googlegroups.com
Hi Fer,

There are potentially a number of solutions to your requirement. I will comment on one which is my current preferred approach;

I'd recommend looking at a tutorial using Angularjs as a UI framework. In particular it supports a variation of Model View Controller and once you have the data in the model, then what you want to achieve is very straightforward using the ngRepeat directive. You may need to consider how you load this business data, eg you may need to expose an API/service for retrieving the data as say a REST API call. An advantage of this approach is Angular has built in capability for managing integration to REST API's.

Note however that this may be easier done as an external form (which is not that difficult) as Im not 100% up to date with the current restrictions on embedded forms.

If you want further inspiration, Tasklist and Cockpit use this architectural style, however IMHO, these are sophisticated implementations and may present a steep learning curve if you're not yet familiar with AngularJS

regards

Rob

Fer

unread,
Feb 25, 2014, 5:15:02 AM2/25/14
to camunda-...@googlegroups.com
Hello Rob,

Thanks for your explanation. I've taken a look to the whole Angular.js tutorial and now it seems clear to me how to create the forms with ngRepeat and so forth. But on the other hand, what's not clear to me is how I can connect the information from Camunda BPM to these scripts. I know that if I have something like this:
        <h2>Inventory</h2>  
       
<div class="control-group">
           
<label class="control-label" for="inventory">List of assets:</label>
           
<div class="controls">        
               
<textarea form-field name="inventory" rows="5" readonly="true"></textarea>        
           
</div>
       
</div>    

And "inventory" is an execution variable with any data, the data is shown as the value of the textarea, but I don't know how to communicate/store it to use it with Angular.js in the rest of the html. How can I print the value of those execution variables into the form? Is that what you meant about the REST API call? Should I create a service just for that so I don't use execution variables for that and load the values through a HTTP request as explained in the AJS tutorial?

Thanks for your help. It's amazing having a nice community who to learn from since there are not a lot of examples of these concrete things yet.


Kind regards.

webcyberrob

unread,
Feb 25, 2014, 6:43:04 AM2/25/14
to camunda-...@googlegroups.com
Hi Fer

Let me walk through a hypothetical example to help elaborate...

Lets assume my use case is I have a customer order with many line items and I wish to display the line items as rows on an HTML form.

I am a big advocate of storing business object data outside of the process engine. Hence in the use case above, I would typically persist the customer order object in its own database and I would create a component which creates A Restful API style for CRUD operations on the customer order. In particular, lets assume I have a get request which given an ID for an order, returns the order content as a JSON stream (eg I have a URL /order/{orderID})

Now assume I have a process instance operating on the order and thus the orderID is the only process variable (and typically this would be the businessKey).

Now let me articulate the pattern for say an external form constructed using the Angular framework. Hence from tasklist, the browser will be redirected to the external form and one of the parameters will be a reference to current task. Hence my Angular app can retrieve the task Id from the URL. Thus now the controller in my Angular form app ca call the engine Rest API to get the task details (/task/{id}) which returns the task details including the businessKey. So now that I have the businessKey, my controller can call the business object Rest API to get the business object data (eg /order/{orderId}) and now that I have the order content returned as a business object and loaded it into my Angualr model, given the binding between model and view, my form will now use the ngRepeat directive to render the content onto my form.

This sounds a little complex, but its a powerful pattern and whilst there is room for improvement and optimisation, hopefully this will elaborate the concepts.

Note that we haven't yet started on a post back to update the business object and mark the task complete...

Rob

Fer

unread,
Feb 25, 2014, 8:01:58 AM2/25/14
to camunda-...@googlegroups.com
Thanks again for your thorough explanation, Rob. I understand all that you have said, and it is very similar to what I had thought from the beginning, but I didn't know exactly how to start. Now I had some other ideas and doubts...

1. Is there any way to reference/print any process variable within an embedded external form? If the input fields from the forms can show the info, can't we do the same?

2. Is it a good idea or could we store a whole JSON with all the information as a process variable of type string? If 1) is true, then we could just echo the variable within a JS script and process the JSON to get all the required info. But then showing the information in the cockpit app would be a mess, I guess.

3. Would we have to post the info through the custom REST API too?

Furthermore, I guess it would be better to call directly to the custom REST API with the taskID from the URL and communicate internally with the BPM engine to get the process variables and then the external info. That is, we would get the process variables from the process with that taskID of the request, and with those process variables referring the inventory in this case, we could use a service or something similar to retrieve the information from our custom database). Would be better to use the two APIs to be more independent, optimal or well-designed?


Thanks for all your answers and your time. I hope this helps other people too.

Fer

unread,
Feb 26, 2014, 7:37:34 AM2/26/14
to camunda-...@googlegroups.com

Hello,

I've been researching for a while and I've found this:

https://groups.google.com/d/msg/camunda-bpm-users/AzC7-tkK15M/nSUsiH7_R7UJ

I guess my application would be case b-right (since I have a database for Camunda and another one for my data).




Anyway, could anyone answer my previous questions? Thanks a lot.

webcyberrob

unread,
Feb 27, 2014, 3:56:09 AM2/27/14
to camunda-...@googlegroups.com
Hi Fer,

With regard to question
1:
I think what you are asking is can you retrieve any/all process variables via REST API - yes, just use the task instance to get the process instance, then call the REST API to get all the process variables. Hence once you have all the variables, choose which ones and how to display in your form.

2:
I don't see why not, but as you say, there is consequence with this design choice.

3:
If your form updates the 'business object', then yes you need to persist the business object data in its database, and signal the process that the user task is complete. Ideally you want these two operations to occur in a transaction. Hence if you orchestrate this from the Angular client, you need to orchestrate your own distributed transaction. If you write a custom API, then its potentially easier and more robust as the coordination is occuring server side...

Fer

unread,
Feb 27, 2014, 5:27:10 AM2/27/14
to camunda-...@googlegroups.com
Thanks for your answer, Rob,

About the first question, I did not mean that. I was asking if I could use an embedded form directly in a User Task, and access to a process variable from inside. For example, if this is custom-form.html:

<div class="span12">
   
<div class="alert alert-info">${customerName} is currently doing task 23.</div>
</div>

The only way I currently know is to use a read-only input text within a form where the name attribute is the process variable, which reduces the functionality of the system...


Anyway, thanks for all your advice. It really helped me to choose a system design for all I wanted to do using Camunda. Finally, I'll write a custom API with just the needed functions for each task. It seems the best choice here for my needs.



Best regards.

webcyberrob

unread,
Feb 27, 2014, 6:02:29 AM2/27/14
to camunda-...@googlegroups.com
Hi Fer,

I see what you mean now - I believe so, from the user guide...

Displaying the value of a form variable

You can reference the value of a process variable using the formVariable() method:

The user {{formVariable('selectedName')}} should approve this request!

regards

Rob
Message has been deleted

Fer

unread,
Mar 18, 2014, 8:13:14 AM3/18/14
to camunda-...@googlegroups.com
Hello again,

I've implemented the custom API above explained and it works when I use an AJAX request from my forms, but I still have something that I can't make it work. Since it's part of the integration of Tasklist (and not just Angular.js) with a custom system, I post it on these forums so somebody may help me or may be helped in the future.

I don't manage to create the Angular.js application within the Tasklist module that works as wanted. I'm not an expert (first time using it), but I've read a lot of tutorials and pages about Angular.js all over these days and nothing seems to work. If I try to inject values into the $scope value and within the context of the function I can check the console and see that it's storing the values, but outside of the function I get null.

So far I've tried to create a new app and bootstrap it to have several modules in the same page (source). Partially based on this.

 <div class="row-fluid" id="inventoryRoot" ng-app="inventoryApp">
   
<div class="span12">
       
<img src="https://www.google.se/images/srpr/logo11w.png" style="margin-top:10px; margin-bottom:30px" />
   
</div>
   
<div class="span12" >

       
<h2>Inventory</h2>    
       
<div class="control-group">
           
<label class="control-label" for="inventory">List of assets:</label>
           
<div class="controls">        
<!--                 <textarea form-field name="inventory" rows="5" readonly="true"></textarea>         -->
           
</div>
           
<ul>
               
<li ng-repeat="asset in assets">
                    There are {{asset}}.
               
</li>
           
</ul>
       
</div>    
   
</div>


<script form-script type="text/form-script">

// create module and bootstrap it to the main app
var myApp = angular.module("inventoryApp", []);
angular
.bootstrap(document.getElementById("inventoryApp"), ['tasklist']);

// get the module
myApp
= angular.module("inventoryApp");

// add factory to module
myApp
.factory('Inventory', ['$resource',
   
function($resource) {
       
return {getInv: function() {
            $resource
('URL_FROM_REST_SERVICE', {}, {
                query
: {method:'GET', isArray:true}
               
}
           
);
       
}}
       
   
}
]);

// call the function from Inventory with the $scope as argument
function getInventory($scope, Inventory) {
    $scope
.assets = Inventory.getInv();
}


// set assets in scope
getInventory
();

 
</script>




If I try to create a new controller in the code, I just get the typical "Error: Argument ctrlName is not a function, got undefined", which is the obvious and theoretically easy option, but not here, so I need to find a way to set the scope variable with the correct data retrieved with the REST API. If I hardcode the data I can make it work, but not like this. Any hint about what I'm doing wrong and how it should be done? Thanks.



Best regards.
Reply all
Reply to author
Forward
0 new messages