Introduction
Previously we wrote about angular.js and activiti-rest and we covered users and groups. Now we went even further and forned the activiti itself in order to create angular.js activiti client.
Here we are going to explain how to manage tasks. Use cases that are covered are
- Listing tasks of current user as assignee
- Searching tasks
- Opening the task details, showing task name, workflow and form properties
- Creating generic form that can show the task form properties
- Submitting form data
Workflow
For this we have created on workflow process, really easy one simple approve process with some of custom properties.
Process has two tasks Approval Task and Check outcome task. Approval Task has a form defined that has properties of types
- Motivation [String]
- Date [Date]
- Long [Long]
- Boolean [Bool]
Form properties can also be read only, in that case activiti will not allow us to save its new value.
Motivation is in first task ( approval task) editable and allows user to enter text, in second Check outcome task user is not able to change the motivation value.
The End Result
Lets see the end result for tasks list and few ideas that will come up in the future.
As you can see we have simple task list, with data that is really important to the user
- Priority, with bottom border color
- Assignee, user that is assigned
- Task name
- Description
- Due date
Our user Admin has to be able to interact with the UI and finish his tasks. After user clicking on Approval task row, popup appears. Here is the image for this.
Popup dialog shows the user information about the task at hand. After user enters required data and clicks Finish Task, data is saved and task is finished, allowing user to select next task.
User is able to see several popups. Each popup would contain one task user can interact with. In the future here we will have task comments allowing user to collaborate.
Angular.js implementation
Anagular.js being a powerful tool, allowed us to create this using minimal lines of code. We have create factories for
- Listing assigned tasks
- Listing task with specified taskId
- Finishing task
- Loading task form
- Submitting form
Listing assigned tasks
Task listing is done using service
service/runtime/tasks/{taskId}
There are multiple ways of filtering though the data, setting assignee, candidate user, dueDate and so on. We currently support only assigned tasks.
Lets see how is this achieved in angular.js. Creating a factory like this
angular.module('activitiApp').factory('TasksService', function ($resource) { var data = $resource('service/runtime/tasks/:taskId', {taskId: "@taskId"}); return data; });
allows us to list all tasks, and one task setting the taskId value.
Loading all tasks for logged in user is done like this
$scope.loadTasks = function () { $scope.tasks = TasksService.get({"size": 1000,"assignee":$rootScope.username}); }
, our user username object is saved in $rootScope so it can be used in this cases.
Loading one task
Loading one task with userId specified is done using TaskService using next lines of code
TasksService.get({"taskId": task.id}, function (data) { //here do our magic with the task data });
Activiti forms
In Activiti you can for each user task specify a form with properties. Each property can be of some type. Types supported are:
- Text
- Boolean
- Long
- Enum
Each property can have metadata specified for each property, in metadata we can specify
- Name
- Value
- Readable
- Writable
- Required
- Date pattern
- EnumValues
Enum values holds list of pairs id and value. Property can have any value from that list. So in form this can be rendered as comboBox or dropDown.
Activiti-rest has two services, one for loading task form and other for submitting. For this we have created FormDataService. Specified below.
angular.module('activitiApp').factory('FormDataService', function ($resource) { var data = $resource('service/form/form-data?taskId=:taskId', {taskId: "@taskId"}); return data; });
Loading task form data is easy as this
FormDataService.get({"taskId": task.id}, function (data) { for (var i = 0; i < data.formProperties.length; i++) { //do magic here } });
Part of response is listed below.
"formProperties":[ { "id":"approved", "name":"Decision", "type":"enum", "value":null, "readable":true, "writable":true, "required":true, "datePattern":null, "enumValues":[ {"id":"true","name":"Approve"},{"id":"false","name":"Reject"} ] } ]
Submitting activiti form data
For submitting form data same FormDataService is used and its save method like specified below
var saveForm = new FormDataService(objectToSave); saveForm.$save(function () { });
We have objectToSave, so lets say that we want to save enum value approved, we would make our objectToSave like this:
{ "taskId":"5", "properties":[ { "id":"approved", "value":"true" } ] }
Submitting activiti form with dates
If one of the properties type is date then we have line of two more. After loading form date have datePattern, our submitting date value must comply with this pattern. For date formatting we have used moment.js, now they have angular-moment that works great.
Lets see the code for converting string to date and to formated string again.
var date = new Date(formProperty.value); elem.value = moment(date).format(formProperty.datePattern.toUpperCase());
Completing the task without submitting forms
Tasks can also be completed without submitting forms, this is done using TaskService like so
var action = new TasksService(); action.action = "complete"; action.$save({"taskId": detailedTask.id}, function () { //after finishing remove the task from the tasks list });
Rendering activiti form
Activiti.js has ngIf, really nice directive that can be used here for rendering activiti task form. Each form property is of one of specified type(string, boolean, long, enum), so in case of string we would have next code
<input ng-if="item.type=='string'" ng-visible="item.readable" ng-disabled="{{item.writable == false}}" ng-required="item.required" type="text" class="form-control" placeholder="{{item.name}}" ng-model="detailedTask.propertyForSaving[item.id].value">
propertyForSavig is object that is used to submit form to activiti-rest, that we have explained that before.
Enumeration is a bit tricky, lets dig into the code
<div ng-if="item.type=='enum'" class="btn-group" dropdown > <button type="button" class="btn btn-primary dropdown-toggle" ng-visible="item.readable" ng-enabled="item.writable" ng-required="item.required"> {{detailedTask.propertyForSaving[item.id].value==null? "Select":detailedTask.propertyForSaving[item.id].name}} <span class="caret"></span> </button> <ul class="dropdown-menu" role="menu"> <li ng-repeat="enum in item.enumValues"> <a id="{{enum.id}}" ng-click="setFormEnum(enum,detailedTask.propertyForSaving[item.id])">{{enum.name}}</a> </li> </ul> </div>
As we can see for each enum in enum values render one option.
Function setFormEnum is simple as setting the value.
Summary
We have covered a number of things here, like listing tasks, finishing them, loading task forms and submitting them also.
You may be wondering where is the code, it will be deployed to gitHub soon after we reach beta . For that we must finish few things for claiming the tasks, and listing other tasks where user is not directly assigned but has been involved and so on.
You can read first post of activiti-rest angular.js series here.
If you are developing your angular.js application that consumes data from activiti-rest and have problems with Access-Control-Allow-Origin you can find solution here.
To find our third post angularjs activiti module go here.
It’s very interesting.
can i have complete download of rest client including this second part.
thanks
Hello. Code will be available soon on my github account.
Thank you for the interest!