2
votes

I'm new to Camunda and didn't find any tutorial or reference explaining how to achieve the following:

When starting a process I want the user to add as many items as he likes to an invoice. On the next user task all those items and their quantity should be printed to somebody approving the data.

I don't understand yet how to get this 1:n relationship between a process and its variables working. Do I need to start subprocesses for each item? Or do I have to use a custom Java object? If so, how can I map form elements to such an object from within the Tasklist?

1
And a question: Do you need the list as a Java List variable or do you intend to use it in task forms only (where a JSON string could do)?thorben
Will have a look, thanks. An yes JSON is just fine, but it sounds like I have to write some JS inside the forms to serialize/deserialize this JSON manually.Sebastian S
This blog post may also be helpful: blog.camunda.org/2015/02/…thorben
@thorben Thanks, got most of it working, see remaining issues in my answer below. But these are different questions and don't need to get answered here.Sebastian S

1 Answers

5
votes

I got it working with the help of the links provided by Thorben.

The trick is to use JSON process variables to store more complex data structures. I initialize such lists in my "Start Event". This can be done either in a form or in my case in a Listener:

execution.setVariable("items", Variables.objectValue(Arrays.asList(dummyItem)).serializationDataFormat("application/json").create());

Note that I added a dummyItem, as an empty list would lose its type information during serialization.

Next in my custom form I load this list and can add/remove items. Using the camForm callbacks one can persist the list.

<form role="form" name="form">
    <script cam-script type="text/form-script">
    /*<![CDATA[*/
    $scope.items = [];

    $scope.addItem = function() {
        $scope.items.push({name: '', count: 0, price: 0.0});
    };

    $scope.removeItem = function(index) {
        $scope.items.splice(index, 1);
    };

    camForm.on('form-loaded', function() {
        camForm.variableManager.fetchVariable('items');
    });

    // variables-fetched is not working with "saved" forms, so we need to use variables-restored, which gets called after variables-fetched
    camForm.on('variables-restored', function() {
        $scope.items = camForm.variableManager.variableValue('items');
    });

    camForm.on('store', function() {
        camForm.variableManager.variableValue('items', $scope.items);
    });
    /*]]>*/
    </script>


    <table class="table">
        <thead>
            <tr><th>Name</th><th>Count</th><th>Price</th><th></th></tr>
        </thead>
        <tbody>
            <tr ng-repeat="i in items">
                <td><input type="text" ng-model="i.name"/></td>
                <td><input type="number" ng-model="i.count"/></td>
                <td><input type="number" ng-model="i.price"/></td>
                <td>
                    <button class="btn btn-default" ng-click="removeItem($index)">
                        <span class="glyphicon glyphicon-minus"></span>
                    </button>
                </td>
            </tr>
        </tbody>
        <tfoot>
            <tr>
                <td colspan="4">
                    <button class="btn btn-default" ng-click="addItem()">
                        <span class="glyphicon glyphicon-plus"></span>
                    </button>
                </td>
            </tr>
        </tfoot>
    </table>

</form>

Two things aren't working yet:

  • Field validation, e.g. for number fields
  • The dirty flag used on the "save" button doesn't get updated, when adding/removing rows