Activiti User Guide(Activiti用戶指南)-Chapter 08. Forms(表單)
Posted on 2010-11-28 19:49 網(wǎng)路冷眼@BlogJava 閱讀(14695) 評論(6) 編輯 收藏 所屬分類: BPMChapter 08. Forms(表單)
Table of Contents
Activiti provides a convenient and flexible way to add forms for the manual steps of your business processes. We support two strategies to work with forms: Build-in form rendering and external form rendering
Activiti為你的業(yè)務(wù)流程的手動步驟提供了一個方便和靈活的增加表單的方法。我們?yōu)楸韱蔚墓ぷ髦С謨煞N策略:內(nèi)置渲染和外部渲染。
Build-in form rendering(內(nèi)置表單渲染)
Build-in form rendering is the simplest to get started with. We'll explain it with an example.
最簡單的開始的方式是內(nèi)置表單的渲染。我們將以一個示例進行解釋。
The demo setup script installs the vacationRequest business process as an example of using task forms through Activiti Explorer. Please check the example for the complete source code. The business process diagram looks like this:
作為通過Activiti Explorer使用任務(wù)表單的一個示例,demo setup腳本安裝了業(yè)務(wù)流程。請檢查示例的完整的源代碼。業(yè)務(wù)流程圖如下所示:
To use the build-in rendering, the form files have to be included in the deployment. That can be done programmatically like this:
為了使用內(nèi)置的渲染,在部署中必須包括表單文件。通過如下的編程方式來完成:
Deployment deployment = repositoryService.createDeployment() .addClasspathResource("org/activiti/examples/taskforms/VacationRequest.bpmn20.xml") .addClasspathResource("org/activiti/examples/taskforms/approve.form") .addClasspathResource("org/activiti/examples/taskforms/request.form") .addClasspathResource("org/activiti/examples/taskforms/adjustRequest.form") .deploy();
Or that can be done with an ant task by first building a business archive zip file that contains the process and forms like above and then deploy it like this:
或者,首先通過構(gòu)建一個包含如上所示的流程和表單的業(yè)務(wù)檔案zip文件,然后,如下部署這個文件。這些通過一個ant任務(wù)來完成。
<taskdef name="deploy-bar" classname="org.activiti.engine.impl.ant.DeployBarTask"> <classpath> <pathelement path="files/demo"/> <fileset dir="build"> <include name="activiti-cfg.jar"/> </fileset> <fileset dir="${activiti.home}/examples/activiti-engine-examples/libs-runtime" /> <fileset dir="${activiti.home}/examples/activiti-engine-examples/libs-test" /> </classpath> </taskdef> <deploy-bar file="${activiti.home}/setup/build/activiti-examples.bar" />
The BPMN 2.0 specification does not specify how tasks or task forms should be rendered, as such forms are defined using Activiti specific constructs. There is the attributeuserTask
sthat can be specified on startEvent
s and userTask
s.
BPMN 2.0 規(guī)范并沒有指明任務(wù)或者任務(wù)表單應(yīng)當(dāng)如何渲染,使用Activiti特定的構(gòu)造型來定義如此的表單。在和 startEvent
s和 userTask
之上能夠制定這個屬性 userTask
。
<startEvent id="request" activiti:formKey="org/activiti/examples/taskforms/request.form" /> <sequenceFlow id="flow1" sourceRef="request" targetRef="handleRequest" /> <userTask id="handleRequest" name="Handle vacation request" activiti:formKey="org/activiti/examples/taskforms/approve.form" > <documentation> Vacation request by ${employeeName} </documentation> ... </userTask>
The activiti:formKey
attribute can contain any text value which you use to identify your form in case you do your own form rendering. But the build-in form rendering expects the activiti:formKey
to be a reference to a resource in the same business archive (= deployment).
當(dāng)你采用自己的渲染時,屬性能夠包含你所用來表明你的表單的任何文本值。但是內(nèi)置的表單渲染器猜想activiti:formKey
引用到同一業(yè)務(wù)檔案(=部署)的一個資源。
Activiti Explorer uses the build-in form rendering engines. Currently, there is only one form rendering engine configured, which is Juel. So it resolves resource files as a Juel expression and the resulting HTML String is sent to the client. In the future, we hope to add a FreeMarker form engine, but that will require more library dependencies so we opt for Juel as the default form engine.
Activiti Explorer使用內(nèi)置的表單渲染引擎。當(dāng)前,只有一個以配置的表單渲染器。這個渲染器叫Juel。所以它把資源文件解析為a Juel expression 。解析結(jié)果 HTML String 被發(fā)送到客戶。未來,我們希望加入表單引擎,但是它需要更多的庫依賴,所以我們優(yōu)選把Juel作為缺省的表單引擎。
Here is the rendered form defined in resource org/activiti/examples/taskforms/request.form
. Its a form to capture the data necessary to start a new process instance.
這里是在資源org/activiti/examples/taskforms/request.form
里定義的渲染過的表單。它是一個捕獲啟動一個新的流程實例所必須的數(shù)據(jù)的表單。
And here is the contents of that form file:
這里是那個表單文件的內(nèi)容:
<h1>Vacation Request</h1> Employee Name:<br/> <input type="text" name="employeeName" value="" /> <input type="hidden" name="employeeName_required" value="true" /> Number of days:<br/> <input type="number" name="numberOfDays" value="1" /> <input type="hidden" name="numberOfDays_type" value="Integer" /> First day of vacation:<br/> <input type="date" name="startDate" /> <input type="hidden" name="startDate_type" value="Date" /> Date of return to work:<br/> <input type="date" name="returnDate" /> <input type="hidden" name="returnDate_type" value="Date" /> Vacation pay requested <input type="checkbox" name="vacationPay"/> Vacation pay requested <input type="hidden" name="vacationPay_boolean" value="true"/> Motivation:<br/> <textarea name="vacationMotivation" value=""></textarea>
<EXPERIMENTAL> The hidden fields provide extra information to the Activiti Explorer client application. So the Javascript in the browser will use the hidden fields and enhance the corresponding input fields. For example, it's possible to specify that a certain text field is a date and Activiti Explorer will add a date picker.
<EXPERIMENTAL> 隱藏字段提供Activiti Explorer 客戶應(yīng)用程序的額外信息。這樣瀏覽器將使用這些隱藏字段并加強相應(yīng)輸入字段。例如,制定某一文本字段是date,Activiti Explorer 將加入一個日期選取器是可能的。
-
Variable names must be camel-cased.
變量名必須是駱駝方式的。
-
The default type of the process variable that is stored is string. Use a hidden field with the input variable name followed by '_type' to define the type (and hence also the conversion from the HTML input to the variable):
保存的流程變量的缺省值是 string。使用一個輸入變量后面跟有'_type' 的隱藏字段來定義類型(這樣將HTML的輸入約定為這個變量):
<input type="hidden" name="numberOfDays_type" value="Integer" />
Currently supported are String, Integer, Boolean, Date.
當(dāng)前支持的類型為:String, Integer, Boolean, Date。
-
Input variables can be made required by adding a hidden field with the input variable name followed by '_required':
通過使用一個輸入變量后面跟有'_type' 的隱藏字段來產(chǎn)生必須的輸入變量。
<input type="hidden" name="employeeName_required" value="true" />
-
In Activiti-Explorer, the Date type validates to ISO 8601 (YYYY-MM-DD). This field will also use any native date picker tools in the browser (using the HTML5 input type="date") and fall back on a pop-up date picker using the YUI calendar widget. It is, of course still possible to manually enter your own date, which is validated as you type.
在Activiti-Explorer里,Date類型用ISO 8601 (YYYY-MM-DD)來驗證。這個字段也將使用瀏覽器里的任何本地日期拾取器(使用HTML5的input type="date")。通過YUI calendar widget返回一個彈出日期拾取器。當(dāng)然,也可能手動輸入你自己的日期,由你輸入的驗證方法。
-
The Integer form field in the demo has also been enhanced to make use of HTML5 input type=number, which provides native validation and custom input fields in the supported browser(s), although Activiti-Explorer provides client side validation as well.
在demo里面的Integer表單字段也已經(jīng)被加強產(chǎn)生HTML 5 input type=number。盡管Activiti-Explorer 提供客戶端驗證,但是在可支持HTML5的瀏覽器也提供內(nèi)置驗證和定制輸入字段。
It is expected that Activiti Explorer in the future will use FormService.getStartFormData
instead of these hidden field values to enhance the form input fields. That's why the hidden fields part is marked as experimental. </EXPERIMENTAL>
可以預(yù)料, Activiti Explorer 在未來將會使用 FormService.getStartFormData
來代替這些隱藏字段值來加強表單輸入字段。那是為什么隱藏字段被標(biāo)記為實驗性的原因。. </EXPERIMENTAL>
Use FormService.getRenderedStartForm
to get the rendered form string with the API:
通過API,使用 FormService.getRenderedStartForm
來獲取渲染表單字符串。
String FormService.getRenderedStartForm(String processDefinitionId)
Use FormService.submitStartFormData
to start a new process instance with the properties that the user provided as input for the form:
以一個用戶為表單輸入的屬性,采用 FormService.submitStartFormData
來啟動一個新的流程實例。
ProcessDefinition FormService.submitStartFormData(String processDefinitionId, Map<String, String> properties)
To learn about the difference between starting a new process instance with this FormService
method in comparison with the ProcessInstance RuntimeService.startProcessInstanceById(String processDefinitionId)
, read the section called “Form properties(表單屬性)”
為了了解和 ProcessInstance RuntimeService.startProcessInstanceById(String processDefinitionId)
比較,以這個 FormService
方法啟動一個新的流程實例之間的差異,請參見 read the section called “Form properties(表單屬性)” 。
After submitting the form, a process instance is started and now someone of the management team needs to handle the request.
在提交這個表單之后,一個流程實例被啟動。管理小組的有些人需要處理這個請求。
The corresponding user task has a task form attached to it, which uses the variables which were given as input by the employee in the start form. These variables are referenced as expressions and are resolved at runtime to their text representation.
相關(guān)的用戶任務(wù)具有和它綁定的任務(wù)表單。。這些作為表達式引用,在運行期解析為它們的文本表示。
<h1>Vacation Approval</h1> <p> ${employeeName} would like to take ${numberOfDays} day(s) of vacation. </p> <p> Motivation: ${vacationMotivation} </p> <p> Do you approve this? <select name="vacationApproved"> <option value="true">Yes</option> <option value="false">No</option> </select> <input type="hidden" name="vacationApproved_type" value="Boolean" /> </p> <p> <label> Motivation:<br/> <textarea name="managerMotivation" value=""></textarea> </label> </p>
The manager will now indicate in the form whether the vacation request is approved or not, by selecting the appropriate input in the form.
通過在表單里面選擇合適的輸入,經(jīng)理將在表單里面指示休假請求是否批注。
The result is stored in as a process variable, which is then used in the exclusive gateway after the form is submitted.
結(jié)果作為一個流程變量保存,在表單提交之后在總網(wǎng)關(guān)里使用。
<sequenceFlow id="flow5" sourceRef="requestApprovedDecision" targetRef="adjustVacationRequestTask"> <conditionExpression xsi:type="tFormalExpression">${!vacationApproved}</conditionExpression> </sequenceFlow>
Depending on the input of the manager in the user task, the employee will now get a new task in his personal task list, stating that the vacation request was disapproved and it needs can be refilled if wanted.
依賴在用戶任務(wù)里面經(jīng)理的輸入,雇員現(xiàn)在將在他的個人任務(wù)列表里面得到一個新的任務(wù)。說明休假請求被拒絕。如果再想休假,需要重填。
The employee can now choose to resend the vacation request, which brings process execution again in the user task for the manager. Or the employee can throw away the request, ending the process.
雇員現(xiàn)在能夠選擇重發(fā)休假請求。這樣讓經(jīng)理的用戶流程重新執(zhí)行。或者雇員可以拋棄這個請求,終止這個流程。
<h1>Adjust vacation Request</h1> <p> Your manager has disapproved your vacation request for ${numberOfDays} days. <br/> Reason: ${managerMotivation} </p> <p> Number of days:<br/> <input type="text" name="numberOfDays" value="${numberOfDays}" /> <input type="hidden" name="numberOfDays_type" value="Integer" /> </p ...
External form rendering(外部表單渲染)
Above we showed the build-in task form rendering. But the API also allows for you to perform your own task form rendering outside of the Activiti Engine. These steps explain the hooks that you can use to render your task forms yourself.
上面我們展示了內(nèi)置的任務(wù)表單渲染。但是API也允許你從 Activiti Engine之外執(zhí)行你自己的任務(wù)表單渲染器。這些步驟解釋了你能使用的渲染你的任務(wù)表單的鉤子。
Essentially, all the data that's needed to render a form is assembled in one of these two service methods: StartFormData FormService.getStartFormData(String processDefinitionId)
andTaskFormdata FormService.getTaskFormData(String taskId)
.
必要的,渲染一個表單所需的所有數(shù)據(jù)被組裝在這連個服務(wù)方法的其中之一:StartFormData FormService.getStartFormData(String processDefinitionId)
和TaskFormdata FormService.getTaskFormData(String taskId)
Submitting form properties can be done with ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties)
and void FormService.submitStartFormData(String taskId, Map<String,String> properties)
通過ProcessInstance FormService.submitStartFormData(String processDefinitionId, Map<String,String> properties)
和 void FormService.submitStartFormData(String taskId, Map<String,String> properties)
方法能夠完成提交表單的屬性。
To learn about how form properties map to process variables, see the section called “Form properties(表單屬性)”
為了學(xué)習(xí)表單屬性如何映射到流程變量,參見the section called “Form properties(表單屬性)” 。
You can just stick any form template resource inside the business archives that you deploy (in case you want to store them versioned with the process). It will be available as a resource in the deployment. You can use the String ProcessDefinition.getDeploymentId()
and InputStream RepositoryService.getResourceAsStream(String deploymentId, String resourceName);
to obtain the file that you included in the deployments. That could be your form template definition file.
Btw, you can use this capability of accessing the deployment resources beyond task forms for any other purposes as well.
隨便,為了其它任何目的,你能使用出除了任務(wù)表單之外訪問部署資源的能力。
The attribute <userTask activiti:formKey="..."
is exposed by the API through String FormService.getStartFormData(String processDefinitionId).getFormKey()
and String FormService.getTaskFormData(String taskId).getFormKey()
. You could for instance store a generic key in the form attribute and apply an algorithm or transformation to get to the actual template that needs to be used. This might be handy when you want to render different forms for different UI technologies like e.g. one form for usage in a web app of normal screen size, one form for mobile phone's small screens and maybe even a template for a IM form or an email form.
屬性 <userTask activiti:formKey="..."
通過 String FormService.getStartFormData(String processDefinitionId).getFormKey()
和 String FormService.getTaskFormData(String taskId).getFormKey()
被API 暴露。你可能為實例在表單里面保存一個通用鍵,應(yīng)用一個算法或者轉(zhuǎn)換來得到實際所需的模板。當(dāng)你想從不同的UI技術(shù)渲染不同的表單,這可能方便。例如,一個表單用來在正常屏幕大小的Web應(yīng)用使用;一個表單在手機小屏幕使用,甚至是IM表單或者email表單的一個模板。
Form properties(表單屬性)
All information relevant to a business process is either included in the process variables themselves or referenced through the process variables. Activiti supports complex Java objects to be stored as process variables like Serializable
objects, JPA entities or whole XML documents as String
s.
與業(yè)務(wù)流程相關(guān)的所有信息既可包含流程變量自己,也可通過流程變量包括它們的引用。Activiti支持將復(fù)雜的Java對象保存為像 Serializable
對象,JPA實體或者作為String 的整個XML文檔的流程變量。
Starting a process and completing tasks is where people are involved into a process. Communicating with people requires forms to be rendered in some UI technology. In order to facilitate multiple UI technologies easy, the process definition can includes the logic of transforming of the complex Java typed objects in the process variables to aMap<String,String>
of properties.
啟動一個流程和完成任務(wù)是人們介入到流程的地方。和人通信需要采用一些UI技術(shù)來渲染表單。為了讓多個UI技術(shù)和平共處,流程定義能夠包含轉(zhuǎn)化邏輯:將流程變量里面的復(fù)雜對象傳化為一個屬性的Map<String,String>
。
Any UI technology can then build a form on top of those properties. The properties can provide a dedicated (and more limited) view on the process variables. The properties needed to display a form are available in the FormData returnvalues of StartFormData FormService.getStartFormData(String processDefinitionId)
and TaskFormdata FormService.getTaskFormData(String taskId)
. Those properties are obtained from the process variables.
然后任何UI技術(shù)能在這些屬性之上構(gòu)建一個表單。這些屬性能在這些流程變量之上提供一個專有(有更多限制)視圖。顯示一個表單所需的屬性在返回StartFormData FormService.getStartFormData(String processDefinitionId)
和 TaskFormdata FormService.getTaskFormData(String taskId)
. 的FormData 值里。這些屬性從流程變量取得。
By default, the build-in form engines, will 'see' the properties as well as the process variables. So there is no need to declare task form properties if they match 1-1 with the process variables. For example, with the following declaration:
缺省地,內(nèi)置的表單引擎,將‘看見‘屬性,正如看見流程變量一樣。所以,如果任務(wù)表單屬性和流程變量是1-1的匹配關(guān)系,沒有必要對它聲明。
<startEvent id="start" />
A form will see all the process variables but the formService.getStartFormData(String processDefinitionId).getFormProperties()
will be empty.
In the above case, all the submitted properties will be stored as process variables. This means that by simply adding a new inputfield in the form, a new variable can be stored.
表單將看見所有的流程變量,而 formService.getStartFormData(String processDefinitionId).getFormProperties()
將為空。
Properties are derived from process variables, but they don't have to be stored as process variables. For example, a process variable could be a JPA entity of class Address. And a form property StreetName
used by the UI technology could be linked with an expression #{address.street}
盡管屬性從流程變量繼承而來,但是它們不必存儲為流程變量。例如,一個流程變量可能是一個類Address的JPA實體。并且,由UI技術(shù)使用的表單屬性 StreetName
能夠和一個表達式#{address.street}
連接。
Analogue, the properties that a user is supposed to submit in a form can be stored as a process variable or as a nested property in one of the process variables with a UEL value expression like e.g. #{address.street}
.
Analogue the default behavior of properties that are submitted is that they will be stored as process variables unless a formProperty
declaration specifies otherwise.
Also type conversions can be applied as part of the processing between form properties and process variables.
類型約定能夠應(yīng)用作為表單屬性和流程變量之間處理環(huán)節(jié)的部分。
For example:
例如:
<userTask id="task" <extensionElements> <activiti:formProperty id="room" /> <activiti:formProperty id="duration" type="long"/> <activiti:formProperty id="speaker" variable="SpeakerName" writable="false" /> <activiti:formProperty id="street" expression="#{address.street}" required="true" /> </extensionElements> </userTask>
-
Form property
room
will be mapped to process variableroom
as a String.表單屬性
room
:將作為一個String映射為流程變量room
。 -
Form property
duration
will be mapped to process variableduration
as a java.lang.Long表單屬性
:將作為一個java.lang.Long映射為流程變量duration
duration
。 -
Form property
speaker
will be mapped to process variableSpeakerName
. It will only be available in the TaskFormData. If property speaker is submitted, an ActivitiException will be thrown. Analogue, with attributereadable="false"
, a property can be excluded from the FormData, but still be processed in the submit.表單屬性會將
speaker
映射為流程變量SpeakerName
。他只能在TaskFormData獲得。如果提交屬性speaker,那么將會拋出ActivitiException。模擬地,帶有屬性readable="false"
,盡管一個屬性將會從 FormData排除在外,但是在提交里面仍將處理。 -
Form property
street
will be mapped to Java bean propertystreet
in process variableaddress
as a String. And required="true" will throw an exception during the submit if the property is not provided.表單屬性
street
在流程變量里面將會作為String映射為Java bean 屬性street
。如果沒有提供這個屬性,在提交期間, required="true" 將拋出一個異常。
It's also possible to provide type meta data as part of the FormData that is returned from methods StartFormData FormService.getStartFormData(String processDefinitionId)
andTaskFormdata FormService.getTaskFormData(String taskId)
提供類型元數(shù)據(jù)作為從StartFormData FormService.getStartFormData(String processDefinitionId)
和 TaskFormdata FormService.getTaskFormData(String taskId)
返回的FormData 的部分也是有可能的。
We support following form property types:
我們支持下列表單屬性類型:
-
string
(org.activiti.engine.impl.form.StringFormType) -
long
(org.activiti.engine.impl.form.LongFormType) -
enum
(org.activiti.engine.impl.form.EnumFormType) -
date
(org.activiti.engine.impl.form.DateFormType)
For each form property declared, the following FormProperty
information will be made available through List<FormProperty> formService.getStartFormData(String processDefinitionId).getFormProperties()
and List<FormProperty> formService.getTaskFormData(String taskId).getFormProperties()
對于每個聲明的表單屬性,通過 List<FormProperty> formService.getStartFormData(String processDefinitionId).getFormProperties()
和and List<FormProperty> formService.getTaskFormData(String taskId).getFormProperties()
產(chǎn)生如下的 FormProperty
信息。
public interface FormProperty { /** the key used to submit the property in {@link FormService#submitStartFormData(String, java.util.Map)} * or {@link FormService#submitTaskFormData(String, java.util.Map)} */ String getId(); /** the display label */ String getName(); /** one of the types defined in this interface like e.g. {@link #TYPE_STRING} */ FormType getType(); /** optional value that should be used to display in this property */ String getValue(); /** is this property read to be displayed in the form and made accessible with the methods * {@link FormService#getStartFormData(String)} and {@link FormService#getTaskFormData(String)}. */ boolean isReadable(); /** is this property expected when a user submits the form? */ boolean isWritable(); /** is this property a required input field */ boolean isRequired(); }
For example:
例如:
<startEvent id="start"> <extensionElements> <activiti:formProperty id="speaker" name="Speaker" variable="SpeakerName" type="string" /> <activiti:formProperty id="start" type="date" datePattern="dd-MMM-yyyy" /> <activiti:formProperty id="direction" type="enum"> <activiti:value id="left" name="Go Left" /> <activiti:value id="right" name="Go Right" /> <activiti:value id="up" name="Go Up" /> <activiti:value id="down" name="Go Down" /> </activiti:formProperty> </extensionElements> </startEvent>
All that information is accessible through the API. The type names can be obtained with formProperty.getType().getName()
. And even the date pattern is available withformProperty.getType().getInformation("datePattern")
and the enumeration values are accessible with formProperty.getType().getInformation("values")
通過API來訪問那個信息。通過formProperty.getType().getName()
獲取類型名;通過formProperty.getType().getInformation("datePattern")
獲取時間模式;通過formProperty.getType().getInformation("values")
獲取枚舉值。