Skip to content

Commit 67bbea0

Browse files
committed
cleanup and Add subform subssort
1 parent a8eaf6f commit 67bbea0

File tree

4 files changed

+206
-53
lines changed

4 files changed

+206
-53
lines changed

README.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,45 @@ To enable server-side validation of a *start-form* or *user-task*:
310310
The plugin has the following parameters:
311311
1. `validationUrl` : The validation url to send submissions to. Defaults to `localhost:8081/validate`.
312312
1. `validationTimeout` : The milliseconds to wait before timeout of the Validation Url HTTP request. Defaults to `10000` 10 seconds.
313-
1. `validationHandler` : The bean instance that will execute the Formio Validation. Defaults to an instance of `SimpleFormioValidationHandler.class`. Override this configuration if you have special validation handling requirements.
313+
1. `validationHandler` : The bean instance that will execute the Formio Validation. Defaults to an instance of `SimpleFormioValidationHandler.class`. Override this configuration if you have special validation handling requirements.
314+
315+
316+
## Subforms / Nested Forms
317+
318+
You can nest forms using the `container` component. The `container` component is used to gather form values into a nested
319+
JSON object.
320+
321+
### Subform usage
322+
323+
1. Add a container to your form
324+
1. In the api properties tab set a component property of:
325+
1. Key: `subform` value: `deployment=mySubForm.json` or `path=/my/path/mySubForm.json`
326+
327+
The `subform` property value uses the same format as the formkey parameters.
328+
329+
Typical use cases is to set the container to "disabled", then the nested form is read-only and you display the
330+
form of a previous submission.
331+
332+
### Hide Subform buttons
333+
334+
The submit button will be hidden by default. If you want to hide all buttons on your subform add a additional property
335+
to the container:
336+
337+
- Key: `hideButtons` value: `true` (this should be a string value)
338+
339+
### Subform Pre-population
340+
341+
If you want to populate the subform with values from a variable, use the `camVariableName` property. You can access previous
342+
form submissions with Key: `camVariableName` value: `myPreviousSubmissionJsonVariable.data`. The data object, which is what
343+
Formio places all submission data into, will then be populated into the subform.
344+
345+
346+
## File Uploads
347+
348+
Use the `file` component, and use the Base64 storage format. The file will be uploaded as part of the JSON submission to camunda.
349+
350+
You can display you file upload in a formio form by returning the base64 value into a readonly/disabled form (such as using subforms).
351+
352+
Typical use case would be to upload the file as JSON/base64 as part of the form submission, and then handle transitive modifications of the file
353+
into other storage formats, and drop the base64 value from the submission / replace with other values pointing to a long term
354+
storage format (such as a blob/file storage container)

bpmn/MyStartForm.json

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,30 @@
4545
}
4646
]
4747
},
48+
{
49+
"type": "day",
50+
"key": "birthdate",
51+
"label": "Birthdate",
52+
"input": true,
53+
"dayFirst": false,
54+
"fields": {
55+
"year": {
56+
"required": false,
57+
"placeholder": "",
58+
"type": "text"
59+
},
60+
"month": {
61+
"required": false,
62+
"placeholder": "",
63+
"type": "select"
64+
},
65+
"day": {
66+
"required": false,
67+
"placeholder": "",
68+
"type": "text"
69+
}
70+
}
71+
},
4872
{
4973
"type": "select",
5074
"key": "select",
@@ -122,30 +146,6 @@
122146
"required": true
123147
}
124148
},
125-
{
126-
"type": "day",
127-
"key": "birthdate",
128-
"label": "Birthdate",
129-
"input": true,
130-
"dayFirst": false,
131-
"fields": {
132-
"year": {
133-
"required": false,
134-
"placeholder": "",
135-
"type": "text"
136-
},
137-
"month": {
138-
"required": false,
139-
"placeholder": "",
140-
"type": "select"
141-
},
142-
"day": {
143-
"required": false,
144-
"placeholder": "",
145-
"type": "text"
146-
}
147-
}
148-
},
149149
{
150150
"type": "button",
151151
"action": "reset",

bpmn/MyUT1.json

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,64 @@
22
"display": "form",
33
"components": [
44
{
5-
"label": "First Name",
6-
"tableView": true,
7-
"key": "firstName",
5+
"label": "Submission (Read-Only)",
6+
"hideLabel": false,
7+
"disabled": true,
8+
"tableView": false,
9+
"key": "previousSubmission",
810
"properties": {
911
"fetchVariable": "postProcessed_submission",
10-
"camVariableName": "postProcessed_submission.data.firstName"
12+
"camVariableName": "postProcessed_submission.data",
13+
"subform": "deployment=MyStartForm.json",
14+
"hideButtons": "true"
1115
},
12-
"type": "textfield",
16+
"type": "container",
17+
"input": true,
18+
"components": []
19+
},
20+
{
21+
"label": "HTML",
22+
"tag": "h3",
23+
"attrs": [
24+
{
25+
"attr": "",
26+
"value": ""
27+
}
28+
],
29+
"content": "Review:",
30+
"refreshOnChange": false,
31+
"tableView": false,
32+
"key": "html",
33+
"type": "htmlelement",
34+
"input": false
35+
},
36+
{
37+
"label": "Review Outcome?",
38+
"optionsLabelPosition": "right",
39+
"inline": false,
40+
"tableView": false,
41+
"values": [
42+
{
43+
"label": "Approved",
44+
"value": "approved",
45+
"shortcut": ""
46+
},
47+
{
48+
"label": "Denied",
49+
"value": "denied",
50+
"shortcut": ""
51+
}
52+
],
53+
"key": "reviewOutcome",
54+
"type": "radio",
55+
"input": true
56+
},
57+
{
58+
"label": "Comments",
59+
"autoExpand": false,
60+
"tableView": true,
61+
"key": "comments",
62+
"type": "textarea",
1363
"input": true
1464
},
1565
{

common/forms/formio.html

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,25 @@
66
<!-- TODO is it better to have the formio div in the <form> element or outside of it? -->
77
<div id="formio"></div>
88

9-
<script cam-script type="text/form-script">
9+
<script cam-script type= "text/form-script">
1010
<!-- <script>-->
1111

1212

1313
let submissionSuffix = '_submission'
14-
let startFormVariableName = 'startForm' + submissionSuffix;
14+
let startFormVariableName = 'startForm' + submissionSuffix
1515
let formLocationDeploymentParam = 'deployment'
16-
let formLocationLocalPathParam = "path"
17-
let fetchVariableKey = 'fetchVariable';
18-
let camVariableNameKey = "camVariableName"
19-
let stringifyKey = "stringify"
20-
let formioDivId = "formio"
21-
let submitAsTransientVariableParam = "transient"
16+
let formLocationLocalPathParam = 'path'
17+
let fetchVariableKey = 'fetchVariable'
18+
let camVariableNameKey = 'camVariableName'
19+
let stringifyKey = 'stringify'
20+
let formioDivId = 'formio'
21+
let submitAsTransientVariableParam = 'transient'
2222
let submitAsTransientVariableDefault = false
23-
let submissionVariableNameParam = "var"
23+
let submissionVariableNameParam = 'var'
2424
let removePrivateValidations = true
25-
let firstSubmit = true;
25+
let firstSubmit = true
26+
let subFormKey = 'subform'
27+
let subFormHideButtonsKey = 'hideButtons'
2628

2729
function getParameterByName(name, url) {
2830
if (!url) url = window.location.href;
@@ -34,33 +36,45 @@
3436
return decodeURIComponent(results[2].replace(/\+/g, ' '));
3537
}
3638

39+
/**
40+
* Removes private/secret marked validations from the provided schema.
41+
*/
3742
function cleanPrivateValidations(schema) {
38-
FormioUtils.eachComponent(schema.components, component => {
39-
if (component.validate && component.validate.customPrivate) {
40-
delete component.validate.custom;
41-
}
42-
})
43+
if (removePrivateValidations){
44+
FormioUtils.eachComponent(schema.components, component => {
45+
if (component.validate && component.validate.customPrivate) {
46+
delete component.validate.custom;
47+
}
48+
})
49+
}
4350
}
4451

45-
function getSchema() {
52+
/**
53+
* Returns a formio schema object
54+
* @returns {Promise<formioShchema>}
55+
*/
56+
function getSchema(processDefId, formUrl) {
4657
return new Promise(resolve => {
4758
inject(['$http', 'Uri', function ($http, Uri) {
48-
let processDefId = camForm.processDefinitionId;
59+
// let processDefId = camForm.processDefinitionId;
4960

5061
$http.get(Uri.appUri('engine://engine/:engine/process-definition/' + processDefId)).then(function (response) {
5162

52-
let formNameDeployment = getParameterByName(formLocationDeploymentParam, camForm.options.formUrl);
63+
// let formNameDeployment = getParameterByName(formLocationDeploymentParam, camForm.options.formUrl);
64+
let formNameDeployment = getParameterByName(formLocationDeploymentParam, formUrl);
5365
let formLocDeployment = function () {
5466
return formNameDeployment !== null;
5567
}
5668

57-
let formNamePath = getParameterByName(formLocationLocalPathParam, camForm.options.formUrl);
69+
// let formNamePath = getParameterByName(formLocationLocalPathParam, camForm.options.formUrl);
70+
let formNamePath = getParameterByName(formLocationLocalPathParam, formUrl);
5871
let formLocPath = function () {
5972
return formNamePath !== null;
6073
}
6174

6275
if (formLocDeployment() === false && formLocPath() === false) {
63-
console.error("No valid form json location was found for formio.")
76+
//@TODO bubble this to formio errors
77+
console.error("No valid form json location was found for formio. (could not deployment or path params in form key)")
6478
}
6579

6680
let formName = formLocDeployment() === true ? formNameDeployment : formNamePath
@@ -103,6 +117,7 @@
103117

104118

105119
function getVariables(varNames) {
120+
//@TODO Add error handling when variables are not found. Should be bubbled to formio form errors view.
106121
return new Promise(resolve => {
107122
if (camForm.taskId != null) {
108123
inject(['$http', 'Uri', function ($http, Uri) {
@@ -139,12 +154,12 @@
139154
function getDefaultValues(variablesStore, submissionObject, schema){
140155
let defaultValues = {data: {}}
141156
FormioUtils.eachComponent(schema.components, component => {
142-
if (component.hasOwnProperty("properties")){
157+
if (component.hasOwnProperty('properties')){
143158
if (component.properties.hasOwnProperty(camVariableNameKey)) {
144159
let defaultValue = _.get(variablesStore, component.properties[camVariableNameKey])
145160
let key = component.key
146161

147-
if (component.properties[stringifyKey] === "true"){
162+
if (component.properties[stringifyKey] === 'true'){
148163
// @TODO do a check if the value is a object!
149164
defaultValue = JSON.stringify(defaultValue)
150165
}
@@ -192,10 +207,53 @@
192207
variableManager.createVariable(submissionVariable);
193208
}
194209

210+
/**
211+
* Used to clean buttons in schemas / typically subforms
212+
* Hides Submit buttons
213+
* Hides all buttons if hideAllButtons equals 'true' (string)
214+
*
215+
*/
216+
function cleanButtons(schema, hideAllButtons){
217+
FormioUtils.eachComponent(schema.components, component => {
218+
if (component.type === 'button') {
219+
if (hideAllButtons === 'true') {
220+
component.hidden = true
221+
} else {
222+
if (component.action === 'submit' || !component.action) {
223+
component.hidden = true
224+
}
225+
}
226+
}
227+
})
228+
}
229+
230+
/**
231+
* Injects subForm components into container form components that have the subForm property
232+
* @param schema
233+
* @returns {Promise<undefined>}
234+
*/
235+
function setupContainerSubForms(schema){
236+
return new Promise(resolve => {
237+
let promises = []
238+
FormioUtils.eachComponent(schema.components, component => {
239+
if (component.type === 'container' && _.has(component.properties, subFormKey)){
240+
let sub = getSchema(camForm.processDefinitionId, '?' + component.properties[subFormKey]).then(subSchema => {
241+
cleanButtons(subSchema, component.properties[subFormHideButtonsKey])
242+
component.components = component.components.concat(subSchema.components)
243+
})
244+
promises.push(sub)
245+
}
246+
}, true)
247+
Promise.all(promises).then(() => {
248+
resolve()
249+
})
250+
})
251+
}
252+
195253

196254
camForm.on('form-loaded', function () {
197255
$scope.options.hideCompleteButton = true
198-
getSchema().then(schema => {
256+
getSchema(camForm.processDefinitionId, camForm.options.formUrl).then(schema => {
199257
let varNames = []
200258
FormioUtils.eachComponent(schema.components, component => {
201259
if (_.has(component.properties, fetchVariableKey)){
@@ -213,6 +271,10 @@
213271
return details
214272
})
215273

274+
}).then(details => {
275+
return setupContainerSubForms(details.schema).then(() => {
276+
return details
277+
})
216278
}).then(details => {
217279
// TODO add a loading text
218280
try {

0 commit comments

Comments
 (0)