Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Table of Contents
minLevel1
maxLevel73
outlinefalse
typelist
printablefalse

What is the Form Config?

The Form Config is the central configuration file of a form. It defines things like:

  • How the fields of a form must be positioned in the view (= the view / layout).

  • Where to load and write the form model from / to.

  • Where to load the /wiki/spaces/DEV/pages/2482176001 from.

  • What to do, after a form has been submitted.

  • Title, description, type and other settings of the form.

Here is an example of how such a form config could look like:

Code Block
languagejson
{
  "title": "Add person",
  "description": "Add a new person",
  "hidden": false,
  "schema": "$uri:property:global/app/tld.domain.myapp/object/person/v1/schema",
  "input":"$uri:property:global/app/tld.domain.myapp/object/person/v1/instance/67b07f3c-e006-4bec-a790-ef25b9fe97df",
  "output": "global/app/tld.domain.myapp/object/person/v1/instance/${vars.property.uuid}",
  "layout": {
    "orientation": "vertical",
    "items": [
      {
      "orientation": "horizontal",
      "items": [
        {
        "field": "firstName"
        },
        {
        "field": "lastName"
        }]
      },
      {
        "field": "age"
      },
      {
        "field": "gender"
      },
      {
        "field": "birthDate"
      }
    ]
  }
}

...

All form configs inside the form folder will be automatically picked - up and shown in the forms overview of an app. For example, here you can see the form Add person automatically listed:

...

Configuration Attributes

The form config contains different attributes in order to specify the form. All of these attributes are described in detail below.

title

The title defines the title of the form to be displayed in the web ui and in other locations.

This can be a static text or an i18n uri like $uri:i18n:myTitle for example.

description

The description is optional and describes the intention of the form.

This can be a static text or an i18n uri like $uri:i18n:myDescription for example.

icon

Each form can have an individual icon. This value must be a code name from the Google Material Icons as listed here: https://fonts.google.com/icons

This value is optional. If not given, the icon with code name ballot is shown as default:

...

color

The color of the form icon is used. If not givenspecified, the secondary color is used, which depends on the style settings. By default this is a blue color like this:

...

The color value can be a hex color hexadecimal value, like this:
"color": "#FFA500"

...

Another example:

"color": "red-8"

...

...

hidden

The schema This optional attribute defines a PIPEFORCE URI which is called in order to retrieve the Form schema for this form.

For example, the attribute could look like this:

Code Block
"schema": "$uri:property:global/app/tld.domain.myapp/object/person/v1/schema"

Which could return a JSON schema like this:

...

languagejson

...

whether the form should be shown as tile inside the app ("hidden": false) or not ("hidden": true).

The form can be still loaded by its URL if required and used embedded in lists or workflows.

If this attribute is missing, "hidden": false is used as a default.

permissions

By default any logged-in user can see the the form using role based access rules (RBAC):

  • RBAC-A: Is member of ROLE_DEVELOPER or

  • RBAC-B: Is member of CAN_APP_<app.name> of the according app and ROLE_USER.

This is the default in case no permissions attribute is defined in the app config. This default can be overwritten by specifying the permissions attribute in the form config. In this case the RBAC-B will be ignored. Instead RBAC-C applies:

  • RBAC-C: User is member of CAN_APP_<app.name> and at least one of the given permission rules is valid.

Example:

Code Block
languagejson
{
  ...
  "permissions": {
    "read": ["ROLE_GUEST"]
  }
}

The attribute read defines a list of permission strings the currently logged-in user must match at least one of. By default such a permission string is the name of a required role. In this example, any logged-in user assigned to ROLE_GUEST and CAN_APP_<app.name> is able to see this form. In case the user is assigned to ROLE_DEVELOPER he can also see this form since this will always apply.

PIPEFORCE URIs as permissions

Status
colourBlue
titleSINCE version 10

Furthermore, also PIPEFORCE URI prefixes are supported to define permissions such as:

  • $uri:group:<groupname> = Logged-in user must be member of group with name <groupname>.

  • $uri:role:<rolename> = Logged-in user must have assigned this role (this is used by default also if no such prefix is used.)

Example:

Code Block
languagejson
{
  ...
  "permissions": {
    "read": ["ROLE_DEVELOPER", "$uri:group:supervisors"]
  }
}

schema

The schema attribute defines a PIPEFORCE URI which is called in order to retrieve the Form schema for this form.

For example, the attribute could look like this:

Code Block
"schema": "$uri:property:global/app/tld.domain.myapp/object/person/v1/schema"

Which could return a JSON schema like this:

Code Block
languagejson
{
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string",
      "title": "First Name",
      "description": "The first name of the person."
    },
    "lastName": {
      "formattype": "datestring",
      "title": "DateLast of birthName",
      "description": "The datelast of birthname of the person."
    },
   }
}

output

The output defines the path in the property store where the form data must be stored as JSON property after the form has been submitted. Example:

Code Block
input": "global/app/tld.domain.myapp/object/person/v1/instance/${vars.property.uuid}"

The PE variable will be interpreted at server side and will be replaced by the uuid of the property.

The form handling pipeline can listen to a property.created event on this path then in order to get informed when a new property was created on this path:

Code Block
pipeline:
  - event.listen:
      eventKey: property.created  "age": {
      "type": "number",      
      "title": "Age",
      "description": "The age of the person."
    },
    "gender": {
      "type": "string",
      "title": "Gender",
      filter: ${body.target.path.contains('/tld.domain.myapp/object/person/v1/instance/')}

The same way it works with property.updated event.

Also see:

input

The input attribute defines a PIPEFORCE URI which is called to retrieve the initial input data for the form in order to set predefined values on the form widgets. For example, the input parameter could look like this in order to load a JSON property from the property store:

Code Block
"input": "$uri:property:global/app/tld.domain.myapp/object/person/v1/instance/fe97df"

Which could return a form data (= model) as JSON to be edited similar to this:

Code Block
languagejson
{
  "firstName": "Alice",
  "lastName": "Smith",
  "age": 20,
  "gender": "female",
  "birthDate": "2003/05/01"
}

forwardOnSuccess

Status
colourRed
titleDeprecated
Use onSuccess instead.

This attribute defines an URL, or a path relative to the portal in order to forward to after the form has been submitted successfully. In case the submit caused an error, the form will stay at current location in order to display the error. In case this attribute is not given, the form will show the default submit success message.

onSuccess

Status
colourBlue
titleSince 9.0

This defines what should happen in case the form was successfully submitted. This is optional. If not defined, the default success message and icon will be shown:

forward

Forwards to a URL or a path relative to the portal. In case the submit caused an error, the form will stay at current location in order to display the error:

Code Block
languagejson
{
  "onSuccess": {
    "action": "forward",
    "url": "http://target/url"
  }
}
  • action - must be set to forward.

  • url - The URL or the path relative to the portal where to forward after success.

message

Shows a text message and an icon in case the form was successfully submitted.

Code Block
languagejson
{
  "onSuccess": {
    "action": "message",
    "text": "$uri:i18n:successMessage",
    "icon": "done",
    "color": "green"
  }
}
  • action - Must be set to message.

  • text - Can contain a static text or an i18n key. If not given, the default text will be shown.
    See: TODO

  • icon - Must be a code name from the Google Material Icon. If not given, the default icon will be shown. See: https://fonts.google.com/icons .

  • color - Defines the color of the icon. If not given, the default icon color will be shown.
    See Colors .

layout

By default, in a form, all widgets of the Form Schema are displayed vertically, each ints own row.

...

Vertical orientation

You can change this default by configuring orientation of the layout in the Form Config.

...

"description": "The gender of the person.",
      "enum": [
        "male",
        "female",
        "neutral"
      ]
    },
    "birthDate": {
      "type": "string",
      "format": "date",
      "title": "Date of birth",
      "description": "The date of birth of the person."
    }
  }
}

output

The output defines the path in the property store where the form data must be stored as JSON property after the form has been submitted. Example:

Code Block
"output": "$uri:property:global/app/tld.domain.myapp/data/person/"

Since path ends in a slash / the form framework identifies this as a new property creation and automatically appends the uuid of the new property as “filename” at the end of the path (since version 10). In versions < 10, you had to add the PE ${vars.property.uuid} at the end. This PE will be interpreted on the server side and replaced by the UUID of the property. This approach is deprecated and will be removed soon.

The form handling pipeline can listen to a property.created event on this path then in order to get informed when a new property was created on this path:

Code Block
pipeline:
  - event.listen:
      eventKey: property.created
      filter: ${body.target.path.contains('/tld.domain.myapp/data/person/')}

The same way it works with property.updated event.

Also see:

input

The input attribute defines a PIPEFORCE URI which is called to retrieve the initial input data for the form in order to set predefined values on the form widgets. For example, the input parameter could look like this in order to load a JSON property from the property store:

Code Block
"input": "$uri:property:global/app/tld.domain.myapp/data/person/fe97df"

Which could return a form data (= model) as JSON to be edited similar to this:

Code Block
languagejson
{
  "firstName": "Alice",
  "lastName": "Smith",
  "age": 20,
  "gender": "female",
  "birthDate": "2003/05/01"
}

forwardOnSuccess

Status
colourRed
titleDeprecated
Use onSuccess instead.

This attribute defines an URL, or a path relative to the portal in order to forward to after the form has been submitted successfully. In case the submit caused an error, the form will stay at current location in order to display the error. In case this attribute is not given, the form will show the default submit success message.

onSuccess

Status
colourBlue
titleSince 9.0

This defines what should happen in case the form was successfully submitted. This is optional. If not defined, the default success message and icon will be shown:

forward

Forwards to a URL or a path relative to the portal. In case the submit caused an error, the form will stay at current location in order to display the error:

Code Block
languagejson
{
  "titleonSuccess": "Person",{
    "descriptionaction": "The person formforward",
    ...
"url": "http://target/url"
  }
 "layout}
  • action - must be set to forward.

  • url - The URL or the path relative to the portal where to forward after success.

message

Shows a text message and an icon in case the form was successfully submitted.

Code Block
languagejson
{
  "onSuccess": {
    "orientationaction": "verticalmessage",
    "itemstext": [
      {"field": "firstName"},
      {"field": "lastName"},
"$uri:i18n:successMessage",
     {"fieldicon": "agedone"},
 
    {"fieldcolor": "gender"}
    ]green"
  }
}

This example layout configuration would create exactly the default layout again:

...

Horizontal orientation

You can then change the orientation, width and height of a layout item like this:

Code Block
{
  "title": "Person",
  "description": "The person form",  
  ...
  
  "layout": {
    "orientation": "horizontal",
    "height": "100",
    "width": "900",
    "items": [
      {"field": "firstName"},
      
  • action - Must be set to message.

  • text - Can contain a static text or an i18n key. If not given, the default text will be shown.
    See: TODO

  • icon - Must be a code name from the Google Material Icon. If not given, the default icon will be shown. See: https://fonts.google.com/icons .

  • color - Defines the color of the icon. If not given, the default icon color will be shown.
    See Colors .

layout

By default, in a form, all widgets (= fields) of the Form Schema are displayed vertically, each ints own row.

...

Vertical orientation

You can change this default by configuring orientation of the layout in the Form Config.

To do so, firstly you need to add the element layout to the form configuration as shown in this example:

Code Block
languagejson
{
  "title": "Person",
  "description": "The person form",  
  ...
  
  "layout": {
    "orientation": "vertical",
    "items": [
      {"field": "lastNamefirstName"},
      {"field": "agelastName"},
      {"widthfield": "150age"},
      {"field": "gender"}
    ]
  }
}

...

This would result in this form layout afterwards, where all fields are displayed in a single row (horizontally):

...

Width of 900 in horizontal layout item prevents fields to cover all of the available space.

Layout items and fields in horizontal orientation, by default, try to span as much width as possible, but with respect to neighbouring fields - behave responsively.

Both min-width and max-width can be also used in place of width to reach responsiveness within defined limits.

Nesting layouts and orientations

Layouts and its orientations can be nested in order to create quite complex form structures. Here’s an example:

...

languagejson

...

example layout configuration would create exactly the default layout again:

...

Horizontal orientation

You can then change the orientation, width and height of a layout item like this:

Code Block
{
  "title": "Person",
  "description": "The person form",  
  ...
  
  "layout": {
    "orientation": "horizontal",
    "height": "100",
    "orientationwidth": "horizontal900",
    "items": [
      {"field": "firstName"},
       {"orientationfield": "verticallastName"},
        {"itemsfield": [
          {"field"age", "width": "firstName150"},
          {"field": "agegender"}

       ]
  }
   },
      {
        "orientation": "vertical",
        "items": [
          {"field": "lastName"},
          {"field": "gender"}
        ]
      }
    ]
  } 
}

This example would produce a form like this with nested orientation:

...

type

TODO

Internationalization (i18n)

TODO

Special Form Types

Document Understand Form

TODO

Variable substitution

Request parameters

Note

Status as of May 08th 2023:

  • Not implemented yet

  • Documentation needs to be finalized here once implementation has been finished

It is possible to use request parameters in the form config attributes as placeholders like this:

Code Block
"input": "$uri:command:workflow.history.tasks?taskId=${request.param.myTaskId}"

In order to set the taskId dynamically, one could call the form like this:

Code Block
form?myTaskId=someTaskId

After the placeholder has been replaced, the input parameter will look like this:

Code Block
$uri:command:workflow.history.tasks?taskId=someTaskId

These attributes in the form config support request parameter variable substitution:

  • input

  • output

  • schema

Property attributes (PEL)

Note

Status as of May 08th 2023:

  • It needs to be specified better how this works

  • It needs to be added whether encoding is required any longer

  • Think about to use # for server side placeholders (PEL) and $ for client side (for example ${request.param.abc} )

It is also possible to specify a PEL inside some of the config attributes. This way the PEL will be executed at server side and the result will be used to form the final config value. For example:

Code Block
"output": "global/app/myapp/object/person/v1/instance/#{var.property.uuid}"

In this example the PEL #{var.property.uuid} will be resolved to the UUID of the property which is about to be created when sending this output.

Form Scripting

TODO: Add final description here. See here and add parts of it to this documentation which are finalized: https://logabit.atlassian.net/wiki/spaces/DEV/pages/846233616/PRD11+-+Forms#P1:-Form-script

Internal Script

Use script as a config in form configuration.

Code Block
languagejson
{
  "id": "person",
  "title": "person",
  "public": false,
  "description": "",
  "schema": "$uri:property:global/app/neel/object/person/v1/schema",
  "output": "$uri:property:global/app/neel/object/person/v1/instance/#{property.uuid}",
  "script":{
    "onload":"alert('PipeForce-neel')",
    "onblur":"if (src === 'firstName') { alert(data[src]); console.log('name =>',data[src]) }"
  },
  "layout":{
    "items":[
      {
        "field":"firstName"
      },
      {
        "field":"lastName" 
      }
      ]
  }
}

External Script

Step-1: You have to define path inside script as below given example.

You will get the information about form-data, form-schema, form-layout and source from where the event is triggered below are the param names

data, schema, layout, src

  • data - you will get form model in JSON format

  • schema - you will get the form schema document in JSON format

  • layout - you will get then layout config in JSON format

  • src - it will give the form field name - for better understanding find attached video

  • value - it will give the current value of the element - use for onchange event only

Find the attached video for how to use script in form

...

Code Block
languagejson
{
  "id": "person",
  "title": "person",
  "public": false,
  "description": "",
  "schema": "$uri:property:global/app/pi-4685/object/person/v1/schema",
  "output": "$uri:property:global/app/pi-4685/object/person/v1/instance/#{property.uuid}",
  "script":{
    "path":"$uri:property:global/app/pi-4685/form/script"
  },
  "layout":{
    "items":[
        

The attributes width and height can be also specified on individual widget fields.

This would result in this form layout afterwards, where all fields are displayed in a single row (horizontally):

...

Width of 900 in horizontal layout item prevents fields to cover all of the available space.

In horizontal orientation, layout items and fields, by default, attempt to use maximum width while considering neighboring fields, making them responsive.

Both min-width and max-width can be also used in place of width to reach responsiveness within defined limits.

Nesting layouts and orientations

Layouts and its orientations can be nested in order to create quite complex form structures. Here’s an example:

Code Block
languagejson
{
  "title": "Person Form",
  "description": "A simple person form.",  
  ...
  
  "layout": {
    "orientation": "horizontal",
    "items": [
      {
        "orientation": "vertical",
        "items": [
          {"field": "firstName"},
          {"field": "age"}
        ]
      },
      {
        "orientation": "vertical",
        "items": [
          {"field": "lastName"},
          {"field": "gender"}
        ]
      }
    ]
  } 
}

This example would produce a form with nested orientations like the one shown below:

...

field

Inside a layout element you can place field elements pointing to field ids (widgets). This element can contain additional attributes like these:

hidden

If set to true, the widget is not shown in the form, but the value from the input is sent to the backend on submit. Example:

Code Block
languagejson
      ...
      {
          "orientation": "horizontalvertical",
          "items":[
  
         { "field":"firstName" }, "items": [
            { "field":"lastName" }, "age", "hidden": true}
        ]
   {   },
      ...

readonly

If set to true, the widget value cannot be changed. Example:

Code Block
languagejson
      "field":"gender",...
      {
        "classorientation":"col col-md-5" "vertical",
        "items": [
  }        {"field": "age",  ]"readonly": true}
        ]
}      },
]     } }

Step-2: You have to define script as below example. It will support only these events and add only those events required. For this follow example 2.

...

height

Sets the height of this field, overwriting the default value in case the widget supports this attribute.

Example:

(function () { function onload(data, schema, layout, src)
Code Block
languagejs
json
      ...
      {
        "orientation":  alert('PipeForce PI-4685')"vertical",
    }   function onunload(data, schema, layout, src) { "items": [
        alert('PipeForce PI-4685 unload')
   {"field": "firstName", "height": 20}
  function onkey(data, schema, layout, src) { ]
    console.log('key down') },
 }   function onkeyup(data, schema, layout, src) { ...

width

Sets the width of this field, overwriting the default value in case the widget supports this attribute.

Example:

Code Block
languagejson
       console.log(data[src])...
  }   function onkeypress(data, schema, layout, src) {{
         console.log('key press')
  }"orientation": "vertical",
     function onchange(data, schema, layout, src,value) { "items": [
      if(src == 'gender') console.log('value selecting')
  }
  function onsubmit(data, schema, layout, src) { {"field": "firstName", "width": 30}
        ]
 console.log('Click event')     },
   function  onfocus(data, schema, layout, src) { ...

color

In case this field is a multi-file upload, with color, the color of the file chips can be specified.

Example:

Code Block
languagejson
       alert('on focus')...
    }  {
   // Mouse events   function onmouseoup(data"orientation": "vertical",
schema, layout, src) {      console.log('mouse up')"items": [
    }      function onmousemove(data, schema, layout, src) {{"field": "myFiles", "color": "blue"}
        ]
 console.log('mouse move')     },
     function onmouseover(data, schema, layout, src) {
     alert('mouse over')
  } ...

...

Grouping with title and border

If "border": true, a border is drawn around the current layout element.

If attribute title contains any value this is set as title of a group of the current layout element.

You will usually use both together to create a group of fields. Example:

Code Block
languagejson
      ...
      {
      function onmouseout(data, schema, layout, src) { "orientation": "vertical",
       console.log('mouse out')
  }
  
  function onscroll(data, schema, layout, src) { "title": "Contact details",
       console.log('scrolling')
  } "border": true,
   function onresize(data, schema, layout, src) {
 "items": [
    console.log('resized')   }  return {  {   onload,onunload,onchange,onfocus,onkey,onkeyup,onkeypress,onscroll, onresize,"field": "age" },
             onsubmit, onmouseup, onmousemove, onmouseover, onmouseout{ "field": "gender" },
    }; })()

Example 2

Code Block
languagejs
(function () {   function onload(data, schema, layout, src) { { "field": "birthDate"}
    // Add your code here.  ]
}   function onchange(data, schema, layout},
src,value) {    // Add your code here.
  }
  return { onload, onchange }
 })()  

...

Code Block
languagejs
(function () {
  
  function onchange(data, schema, layout, src,value) {
   pi.pipeline('iam.group.members?name=ALs').then((res)=>{
    debugger
   })
   
  }
  return { onchange }
 })()  

https://quasar.dev/vue-components/select#qselect-api

https://quasar.dev/vue-components/input#qinput-api

https://quasar.dev/vue-components/time#qtime-api

...

 ...

This will produce a grouping with title like this:

...

type

TODO

Internationalization (i18n)

The title and description of a form and also its validation messages can be internationalized (= translated to different languages). See here for more details, how this works: Internationalization (i18n).

Special Form Types

Document Understand Form (with AI support)

TODO

Variable substitution

Request parameters

It is possible to use request parameters in the form config attributes as placeholders, like this:

Code Block
"input": "$uri:command:workflow.history.tasks?taskId=${request.param.myTaskId}"

In order to set the taskId dynamically, one could call the form like this:

Code Block
form?myTaskId=someTaskId

After the placeholder has been replaced, the input parameter will look like this:

Code Block
$uri:command:workflow.history.tasks?taskId=someTaskId

These attributes in the form config support request parameter variable substitution:

  • input

  • output

  • schema

Property attributes (PEL)

It is also possible to specify a PEL inside some of the config attributes. This way the PEL will be executed at server side and the result will be used to form the final config value. For example:

Code Block
"output": "global/app/myapp/object/person/v1/instance/${property.uuid}"

In this example the PEL ${property.uuid} will be resolved to the UUID of the property which is about to be created when sending this output.