Skip to content

Data Model

The definition of the data model is independent of the used perspective(s). It is based on object types and relations common to the backend specific database model. It defines how data is read from the backend and how data is pushed to the backend.

Characteristics:

  • Hierarchical tree structure (of related nodes)
  • One distinct root node (root)
  • Nodes may have several sub nodes (children)
  • Relations are used to traverse from nodes to sub nodes (query)
  • Constraints are used to define the right subset of data records (query)
  • Nodes may have several attributes (persistent, transient) (attributes)

Nodes

Nodes are representing a backend specific entity in an Insight configuration.

Characteristics:

  • Hierarchical tree structure (of related nodes)
  • One distinct root node (root)
  • Nodes may have several sub nodes (children)
  • Relations or queries are used to traverse from nodes to sub nodes
  • Constraints are used to define the right subset of data records

Base properties

The following properties must be set

"name": "Standort"
"type": "LOCATIONS"
"title": "Standort",
"label": "${LOCATION} - ${DESCRIPTION}",
"query": {...}
"name": "Standort",
"type": "508:43137:MC",
"title": "Standort",
"label": "${508:43105:AB}-${508:43345:AB}"
  • name: defines the name of the node
    o unique
    o For technical reasons the name must not contain dots (such as package names).
    o is used for standard aggregation of search results (Insight Explorer)
  • type: defines backend specific object type
  • title: defines label for UI presentation (details (Insight Explorer), header (Insight Mobile))
  • label: is used in perspectives to help users identifying the data record
  • ${ATTRIBUTENAME}: is dynamically replaced with the value of the attribute with the name ATTRIBUTENAME
  • query: is used to describe the subset of data records

query (Maximo)

A node's query-section defines the subset of data that will be retrieved (criteria) and how the data will be retrieved (MboSet or Database).

constraint

constraint constrains the data set that will be retrieved. constraint is optional in root or child nodes. If given, the constraint will be part of the WHERE-clause, so it has to be valid SQL.

"root": {
    "name": "pm",
    "type": "PM",
    "query": {
        "constraint": "PARENT IS NULL"
    },
}
The data set for child nodes is typically constrained by a Maximo relationship. In cases where relations can't be used, constraint comes to the rescue. Placeholders are used to define the parent's properties.

"children": [
    {
        "name": "problemcode",
        "type": "FAILURECODE",
        "query": {
            "constraint": "FAILURECODE='${PROBLEMCODE}'"
        },
    }
]

dynamicConstraint

dynamicConstraint can be used to add a constraint from the database. The WHERE statement from the database is appended to the constraint using AND. The constraint must be chosen on the mobile root list. This is done by a selectFromTree. The selectFromTree.attribute must match backend.primary. There are some restrictions that apply to dynamicConstraint:

  • Index search cannot apply the where statement, so it is not supported. Please use searches
  • Offline search cannot apply the where statement, so you can find all downloaded entries.
  • Insight Mobile only
  • Maximo backend only
  • Root nodes only
{
    "name": "root-node",
    "query": {
        "constraint": "...",
        "dynamicConstraint": {
            "ui": {
                "selectFromTree": {
                    "tree": "where-configs",
                    "node": "config",
                    "attribute": "WHERE_CONFIG_ID"
                }
            },
            "backend": {
                "table": "WHERE_CONFIG",
                "primary": "WHERE_CONFIG_ID",
                "where": "WHERE_CLAUSE"
            }
        }
    }
}

relation

relation constrains the data set of child nodes to the associated objects. relation must be the name of a Maximo relationship that points from parent's type to child's type.

"children": [{
    "name": "asset",
    "type": "ASSET",
    "query": {
    "relation": "ASSET"
    },
}]

A child node must provide at least one of relation or constraint.

db

db is used to define if data is collected by means of MboSets or by direct database access. db can have one out of three values.

false a node's attribute values as well as the idents leading to it are collected with MboSets
true a node's attribute values as well as the idents leading to it are collected with direct database access
null a node's attribute values are collected with MboSets, but the idents leading to it are collected by direct database access

Full database access:

"query": {
    "db": true
}
This setting yields the best performance. The attribute values will be read directly from the database, bypassing the MBO layer entirely.

Use this setting only when: - you can forgot business logic of the MBO layer - all the node's attributes are persistent

Mixed mode:

"query": {
    "db": null
}

That's the default case, when db is not declared at all. The attribute values will be collected by means of MboSets, so the MBO layer's business rules will be obeyed. You can have transient attributes declared. A parent's child idents, concerning the node in question (or the root idents, if the node is the root node), will be collected with direct database access. In almost all cases, this strategy leads to the same result as if the idents were collected by MboSets.

MboSet-only:

"query": {
    "db": false
}

In this mode, even the idents leading to the node are collected by means of MboSets. Use this mode if and only if the MBO layer's business rules must be obeyed to determine the proper child set (or root set). Downloading many data instances with this setting applied will be considerably slower.

orderBy (deprecated)

orderBy is supported for backward compatibility reasons, but is considered deprecated.

You should use sortBy to define the order in which siblings are listed.

indexer

indexer helps with reconciling the redundant data that are held in elastic search indices. It is used to traverse the tree bottom up. A change in an object can affect its parent's child set. In order to reconsider the parent's child set, the indexer hint is needed. Without defining indexer, changes to the parent's child set will be reflected only with the next full build

"children": [
    {
        "name": "asset",
        "type": "ASSET",
        "query": {
            "relation": "ASSET",
            "indexer": {
                "parentRelation": "ALLWO"
            }
        }
    }
]
stopandnochildren

Optimizing redundant data and cut off nodes and whole branches can be done with stop or noChildren in indexer

"children": [
    {
        "name": "asset",
        "type": "ASSET",
        "query": {
            "relation": "ASSET",
            "indexer": {
                "mode": "noChildren"
            }
        }
    }
]
  • mode noChildren means, that objects for this node will be store in redundant data store (Elastic) but no children.
  • mode stop means, that no objects for this node and no children will be stored.
  • mode stop in root-node means that this treeConfig wont be indexed at all

parentMapping

parentMapping can be used to define foreign keys to parent nodes. This is necessary to create offline objects with subobjects. The foreign key attributes of the child node are replaced after creating the parent object. Example tree config:

{
    "menus": [
        {
            "label": "Workorder",
            "action": "create",
            "icon": "icon-plus",
            "actionValues": [
                {
                    "_node": "workorder",
                    "DESCRIPTION": "Static value"
                }
            ]
        }
    ],
    "root": {
        "name": "workorder",
        "menus": [
            {
                "label": "Worklog",
                "action": "create",
                "icon": "icon-plus",
                "actionValues": [
                    {
                        "_node": "worklog",
                        "DESCRIPTION": "Static value"
                    }
                ]
            }
        ],
        "attributes": [
            {
                "name": "WONUM",
                "label": "Number",
                "type": "String",
                "readonly": true
            },
            {
                "name": "DESCRIPTION",
                "label": "Description",
                "type": "String"
            },
            {
                "name": "SITEID",
                "label": "SITEID",
                "type": "String",
                "control": "disabled"
            }
        ],
        "children": [
            {
                "name": "worklog",
                "query": {
                    "parentMapping": {
                        "RECORDKEY": "WONUM",
                        "SITEID": "SITEID"
                    }
                },
                "attributes": [
                    {
                        "name": "DESCRIPTION",
                        "label": "Ãœberschrift",
                        "type": "String",
                        "control": "createOnly"
                    },
                    {
                        "name": "SITEID",
                        "label": "SITEID",
                        "type": "String",
                        "readonly": true
                    },
                    {
                        "name": "RECORDKEY",
                        "label": "Arbeitsauftrag",
                        "type": "String",
                        "readonly": true
                    }
                ]
            }
        ]
    }
}

Root

  • root: defines the root node of the data model
  • Except properties loop and query there is no difference between a root node and a child node. For further information see chapter Node properties.
  • query: is used to describe the subset of data records, according to the current use case.
{
    "name": "locations",
    "root": {
        "query": {
            ...
        }
    }
}

query (OpenJet)

The root node's query section is used to define which entities should form the tree's top level elements. The root query can be defined in the following ways:

1) Unconstrained: Will result in all entities of root's type

"query": { }

2) By use of a modeled search: Will result in all entities that match the search criteria

"query": {
    "jetSearch": "508:42315:QU"
}
  • jetSearches are not allowed to use LibMethods because of their very dynamic behavior!

3) By constraining the root type: Will result in all entities that match the configured constraint.

"query": {
    "constraint": {
        ...
    }
}

Children

Children are nodes. For further information see -> chapter Node properties. Root nodes and child nodes are almost the same excluding the following properties:

  • query: is used to describe the subset of data records (traverse parent  this child node)
  • Configured with a backend specific relation (and/or a constraint and an optional orderBy property (if Maximo))
  • loop: Node/sub nodes relations may be recursive

loop

Node/sub nodes relations may be recursive.

"children": [
    {
        "loop": true,
        "query": "..."
    }
]
  • query: is specified as described in the topic below (children - query)
  • loop: nodes derive their properties (such as attributes and menus sections) from the parent node;

  • Attention : properties derived from parent node must not be redefined

  • Attention : If parent node provides queryParams, then child-data are expected to fulfill queryParam-constraints too, unless omitQueryParams is set to true.

omitQueryParams

Applies to loop nodes only. If set to true, children are not required to fulfill the parent node's queryParam-constraints.

  • Attention: Setting omitQueryParams to true, may cause a security concern. Since the profile constraint is not applied, it can no more be garantueed, that only intended parties can read corresponding data.

query (OpenJet)

The child node's query section is used to define which entities should form the parent's child elements.

There are two forms of declaration:

Relation:

In this form, the query section has 2 mandatory properties:

relation Refers to the association which joins parent type with child type
direction Indicates if the association is to be traversed as in the static model (TOP_DOWN) or reverse (BOTTOM_UP)

The query section has one optional property

constraint Restricts the associated child elements to the proper subset
Chain:

You will use this alternative in order to 'skip unwanted intermediate nodes' as e.g. attributed link classes in m:n relations.

In this form there is only one mandatory attribute:

"chain" Array of relations that traverse from parent to child

"query": {
    "chain": [
        {
            "relation": "508:43149:AS",
            "direction": "TOP_DOWN",
            "constraint": { ... }
        },
        {
            "relation": "508:43151:AS",
            "direction": "TOP_DOWN",
            "constraint": { ... }
        }
    ]
}
Note that:

  • The first relation of a chain must be adjacent with the parent node
  • Consecutive relations of a chain must be adjacent with each other
  • The last relation of a chain must be adjacent with the child node
  • relation and direction fields are mandatory for all relations of a chain, while constraint fields are optional

queryParams

queryParams are used to refine constraints. queryParams can therefore help to reduce the number of root-records/child-records. While the query-section's constraint is applied independent of the calling user, queryParams are applied in behalf of the calling user. The constraining values are obtained from the calling user's profile at runtime. Example:

queryParams take one of the following forms:

"queryParams": {
    "name": "name",
    "attribute": "attribute"
}

Here, name and attribute are to be replaced by the parameter name and the attribute name respectively.

"queryParams": {
    "and": [
        "c1", 
        "c2" 
    ]
}

Here c1, c2, ... are queryParam-Constraints themselves. The supported logical operators are and and or. You can nest and/or. The array-length must be at least 2. Due to backward-compatibility the historical (pre-2.7) syntax is also supported:

"queryParams": [
    {
        "name": "name",
        "attribute": "attribute"
    }
]

If your node not only uses queryParams, but also has a loop child node, then child-data must also fulfill the queryParams constraint!

queryParams-Example-Maximo

"query": {
    "constraint": "HISTORYFLAG=0 AND STATUS IN ('APPR', 'INPRG')"
}
"queryParams": {
    "and": [
        {
            "name": "siteid",
            "attribute": "SITEID"
        },
        {
            "or": [
            {
                "name": "personid",
                "attribute": "OWNER"
            },
            {
                "name": "persongroup",
                "attribute": "OWNERGROUP"
            }
            ]
        }
    ]
}

Constraints (OpenJet)

Constraints are used to restrict the tree's elements to the proper subset of database records. Constraints can be used in two places:

  • In the root node's query section when no jetSearch is defined to restrict the root elements.
  • In the child node's query section in order to restrict the associated child elements.

"query": {
    "constraint": { ... }
}
Constraints come in two forms:

1) Conditional constraints ("Conditional constraints") as for c1 and c2 where c1 and c2 are constraints themselves

2) Atomar constraints ("Atomar constraints") as for firstname = Peter

Conditional Constraints

The logical operators and, or and not are supported by conditional constraints

"constraint": {
        "and": [
            { ... },
            { ... }
    ]
}       
"constraint": {
        "or": [
            { ... },
            { ... }
    ]
}   
"constraint": {
    "not": {
        ...
    }
}

The array length of and/or is not restricted to two as in the example, but must be at least two. For technical reason, and and or cannot be negated with not.

Atomar Constraints

Atomar constraints take one of the following forms

  • Left operator right
  • Left operator
  • InstanceOf className
  • Relation-constraint

  • The terms left may stand for are attribute expressions
  • The terms right may stand for are literals

Supported binary operators are:

eq left equal to right
ne left not equal to right
match left matches right (Joker-Characters are ? and *)
lt left less than right
le left less than or equal to right
gt left greater than right
ge left greater than or equal to right
in left equal to one of the terms in right enumeration
notIn left not equal to any of the terms in right enumeration

Supported unary operators are:

isNull left undefined
notNull left defined
Constraint Fields

Atomar constraints are defined by the following properties:

Operator-Constraints
attribute Model-Id of the attribute that forms the left expression
operator One of the supported binary or unary operators
value Literal that forms the right expression (not needed for unary operators)
InstanceOf-Constraint:
instanceOf Model-Id of the class
Relation-Constraint:
relation Model-Id of the association which joins the related type
direction Indicates if the association is to be traversed as in the static model (TOP_DOWN) or reverse (BOTTOM_UP)
constraint The constraint that must hold for associated objects. The structure is recursive. Atomar constraints can be used as well as conditional ones.
Constraint Example

Here is a quite comprehensive demo of a child node's constraint:

"children": [
    {
        "name": "Person",
        "type": "508:40321:MC",
        "query": {
            "relation": "508:44617:AS",
            "direction": "TOP_DOWN",
            "constraint": {
                "and": [
                    {
                        "attribute": "508:42915:AB",
                        "operator": "in",
                        "value": [ 10, 20, 30, 40 ]
                    },
                    {
                        "attribute": "508:42235:AB",
                        "operator": "eq",
                        "value": true
                    },
                    {
                        "or": [
                            {
                                "attribute": "508:40323:AB",
                                "operator": "in",
                                "value": [ "Bernhard", "Marc" ]
                            },
                            {
                                "attribute": "508:40325:AB",
                                "operator": "in",
                                "value": [ "Hanke", "Neblung" ]
                            }
                        ]
                    }
                ]
            }
        }
    }
]

SortBy

sortBy is used to define the order in which the client should list siblings. sortBy has the following members:

attribute mandatory String denotes the attribute which defines the order
descending optional boolean, default = false list hi values before low values
thenBy optional Map subordinated ordering definition (recursion)

Examples:

"sortBy": {
    "attribute": "billingDate",
    "descending": true
}
"sortBy": {
    "attribute": "lastname",
    "thenBy": {
        "attribute": "firstname"
    }
}

Only persistent attributes are allowed to define the order.

If sortBy is not defined, then the order will be derived from the node's label. The order is then defined by the label's persistent attributes. Since version 31 it is also possible to use inline-attributes. If you use inline-attributes then it could be necessary to use qualified names for attributes in constraint to avoid ambiguous column names. Otherwise sql-errors could occur.

Advanced properties

insert/create nodes

In some backend systems data base tables have non-canonical names for their key attributes. To enable Insight to present data sets immediately after their creation, it is mandatory to register those key attributes.

"IDKEY": "TICKETUID"

Classification (Maximo)

Classification can be used in every node that is configured with a class using classifications.

"root": {
    "classification": {
        "enabled": true,
        "insertBefore": "attribute"
    }
}

List of classification attributes is dynamically inserted before/after the attribute attribute in the "attributes": [] Array. (Option insertBefore/insertAfter)

showClassifier: The classifier attribute is hidden by default. With this boolean property, the classifier can be shown.

Documents

A node of type Documents represents a special kind of child-node. It enables Insight Mobile clients to

  • capture pictures
  • bind these pictures to its parent node
  • push this data to an upload service

There is a default upload service which performs the following steps (Maximo):

  • Creating of a DOCLINK record
  • Linking this record to the parent node object and
  • Storing the picture to the directory configured in the Maximo System Properties.

With backendParams it is possible to pass additional (parent) node specific meta data to the upload service.

minCount: Number of documents/fotos which have to be uploaded.

attributes: Attributes that can be filled in for a document. These attributes are transferred to the server when uploading. The names are not allowed to overlap with those from backendParams. See also: node attributes.

"documents": [
    {
        "name": "foto",
        "label": "Fotos",
        "type": "Foto",
        "backendParams": {
            "OWNERTABLE": "WORKORDER",
            "OWNERID": "${WORKORDERID}",
            "SITEID": "${userData.siteid}"
        },
        "minCount": 1,
        "attributes": [
            {
                "name": "REQUIRED",
                "label": "Required",
                "type": "String",
                "required": true
            }
        ]
    }
],

ExcludeExpertSearch

To exclude an node from expert search use the following configuration:

"excludeExpertSearch": true

LabelWidth

To set a fix label width for the form. (Works in the new client only with labelBeside)

{
    "labelWidth": "130"
}

InlineOnly

InlineOnly can be set for nodes which are only used for inline fields. These nodes are not displayed on the UI.

"inlineOnly": true,

Attributes

Base properties

The following base properties must be configured.

name ATTRIBUTEID (ATTRIBUTEID specifies the Attribute in the given backend)
label xyz xyz is used in the detail view to help the user to identify the attribute
type Specifies the (Insight specific) data type of the attribute

Types

Attention: Value of type must match the type in the backend.

  • String
  • Boolean
  • Date
  • Text
  • Decimal
  • Base64 (Maximo only)
  • Born Boolean or null.
"root": {
    "name": "Standort",
    "attributes": [
        {
            "name": "LOCATION",
            "label": "Standort",
            "type": "String",
            "readonly": true
        }, 
        {
            "name": "DESCRIPTION",
            "label": "Zusammenfassung",
            "type": "String"
        }, 
        {
            "name": "SITEID",
            "label": "Niederlassung",
            "type": "String",
            "hidden": true      
        }
    ]
}

Read/write

  • readonly: Boolean (default false). The form field is disabled and the insight middleware prevents the field from being written.

    o true --> field is disabled
    o false --> field is enabled

To disable, hide or enable at create - see control.

  • mustWrite: Boolean (default false). Normally, only the changed attributes are sent when saving. With this property, this attribute is always sent.

maxlength, cols, rows

widget: String

  • maxlength of an attribute is defined by its size in the database
  • can be overwritten for specific usecases

widget: textarea`

  • cols and rows can be configured
  • when you define cols you should also define a monospaced font via CSS

include attribute of a subsequent node

Inline:

To Include an attribute of a subsequent node in the detail view of the current node, use the following notation:

"attributes":[
    {
        "name": "nodename/attributename"
    }
]

Nodes can also be flagged with inlineOnly.

Barcode

To include the ability to read barcodes via Cam (smartphone, tablet, webcam, ...) use the barcode property. Example:

"attributes": [
    {
        "name": "LOCATION",
        "label": "Standort",
        "type": "String",
        "barcode": true
    }
]

Supported barcode types on iOS and Android:

Barcode Type Android iOS
QR_CODE x x
DATA_MATRIX x x
UPC_A x x
UPC_E x x
EAN_8 x x
EAN_13 x x
CODE_39 x x
CODE_93 x
CODE_128 x x
CODABAR x
ITF x x
RSS14 x
PDF_417 x x
RSS_EXPANDED x

Phone

To include tel protocol use the following notation:

"attributes": [
    {
        "name": "PHONE",
        "phone": true
    }
]

The attribute value is passed to the tel:// protocol which starts dialing the given number, if there is a phone service available on the client device.

Sms

To include sms protocol use the following notation:

"attributes": [
    {
        "name": "PHONE",
        "sms": true
    }
]

The attribute value is passed to the sms:// protocol which starts the default sms app.

Mail

To include mailto protocol use the following notation:

"attributes": [
    {
        "name": "E-Mail",
        "mail": true
    }
]

The attribute value is passed to the mailto:// protocol which starts the default mail app.

Keypad

Shows a button behind the input control that displays a keypad dialog. The buttons of the keypad can be freely defined. When you click on a button, the text is added to the attribute value. At least the value of the button must be configured. An alternative label, color and delete can also be defined. A keypad can be added to a string attribute.

  • value: Value to add (mandatory)
  • label: to display
  • clear: Clears the whole text. Can be used to replace the whole value.
  • styleClass: Styling the button cell. Can be used to add color to the cell.

Attribute with complex dialpad example:

{
    "name": "PHONE",
    "label": "Beschreibung",
    "type": "String",
    "control": "enabled",
    "keypad": {
        "rows": [
            [ { "value": "1" }, { "value": "2" }, { "value": "3" } ],
            [ { "value": "4" }, { "value": "5" }, { "value": "6" } ],
            [ { "value": "7" }, { "value": "8" }, { "value": "9" } ],
            [
                {
                    "label": "DE",
                    "value": "+49 ",
                    "clear": true,
                    "styleClass": "bg-color-light-blue"
                },
                {
                    "value": "0"
                },
                {
                    "label": "Local",
                    "value": "+49 6201 503 ",
                    "clear": true,
                    "styleClass": "bg-color-light-blue"
                }
            ]
        ]
    }
}

Value lists

  • options: Values
    • dictionary: Values can be listed directly
    • hint: Values retrieved from a backend specific dictionary
  • selectFromTree: Values of attributes retrieved from node items of another Insight Config configuration

options

dictionary: Values can be listed directly

hint: Values of a Domain can be included.

hint: Values of a static Thesaurus can be included.

{
    "name": "500:293:AB",
    "label": "DESCRIPTION",
    "type": "String",
    "options": {
        "hint": "thesaurus={\"name\":\"Priority\"}"
    }
}

Values of a dynamic Thesaurus must be included by defining the values as child nodes.

"children": [
    {
        "name": "reportattributeval",
        "type": "500:11453:MC",
        "icon": "icon-list",
        "label": "${500:293:AB}",
        "query": {
            "chain": [...]
        },
        "attributes": [
            {
                "name": "500:293:AB",
                "label": "",
                "type": "String"
            }
        ]
    }
]

To access these values within option, the hint needs to address the child node and the attribute, separated by a semicolon(';')

{
    "name": "500:11542:AB",
    "label": "Value",
    "type": "String",
    "control": "enabled",
    "options": { 
        "hint": "reportattributeval;500:293:AB" 
    }
}       

SelectFromTree

  • selectFromTree: Values can be node items of another Insight Config configuration
  • Attributes with selectFromTree can't be edited manually. This is the default behavior since 2.13.1.
    • The default behavior can be overridden by
      • setting the global client-config-property "selectFromTreeEnabled"
      • setting the property "enabled" on "selectFromTree" (from Version 2.14)
  • Presents node instances of src Insight Config configuration (tree) in a selection view
    • tree: tree to select from
    • node: node to select from (currently only the root node is supported)
    • attribute: attribute of root node (in the referenced configuration specified in tree)
    • search: search name to use when selecting. The search must be defined in the given tree. See: searches.
    • autofill: Array of attributes to be copied if validation succeed
      • src: name of attribute (in the referenced configuration specified in tree)
      • dst: name of attribute in this node (in the enclosing configuration)
    • filter: Key value mapping to filter the items to select.
    • enabled: true, false. Activate manual inputs
    • deep: true, false. Activate deep tree selection. Only the records specified in node can be selected. The selection starts at the root list by default.
      • contextId: (optional) If the last position in the "selectFromTree-tree" is known at the current record, it can be displayed again. contextId must be set to the uniqueId of the record in the selectFromTree-tree.(last part of the path) Full example below. The parent hierarchy can be resolved online and offline. Online: The search-index is used, offline the downloaded data.
      • contextNode: Alternative node to display.
{
    "name": "CRAFT",
    "label": "Gewerk",
    "type": "String",
    "selectFromTree": {
        "tree": "craft",
        "node": "Craft",
        "attribute": "CRAFT",
        "autofill": [
            {
                "src": "STANDARDRATE",
                "dst": "PAYRATE"
            }
        ]
    }
}
  • Example with filter:
"selectFromTree": {
    "tree": "failurelist-problem",
    "node": "failurelist",
    "attribute": "FAILURECODE",
    "filter": {
        "PARENT": "${FAILURELIST1}"
    }
}
  • Full example with deep, contextId and autofill:
{
    "name": "LOCATION",
    "label": "Standort",
    "type": "String",
    "labelHidden": true,
    "clear": true,
    "selectFromTree": {
        "tree": "test-locations",
        "node": "Standort",
        "attribute": "LOCATION",
        "deep": true,
        "contextNode": "Standort",
        "contextId": "${LOCATIONSIDTEMP}",
        "autofill": [
            {
                "src": "LOCATIONSID",
                "dst": "LOCATIONSIDTEMP"
            }
        ]
    }
},
{
    "name": "LOCATIONSIDTEMP",
    "label": "StandortId",
    "type": "String",
    "formField": true,
    "validation": {
        "js": "if(!value){setValue(getValueForName('LOCATIONS/LOCATIONSID'))}"
    }
},
{
    "name": "LOCATIONS/LOCATIONSID",
    "label": "StandortId",
    "type": "String"
}

Field validation

Required

required: true. The flag enables the required field feature.

The client checks if there is data set in the corresponding input field, decorates the field according to the state (filled/not filled) and prevents a push data operation if such a required field is not filled (and presents the error state via decoration and message to the user).

"attributes": [
    {
        "name": "LOCATION",
        "label": "Standort",
        "type": "String",
        "required": true
    }
]

ExistsValidator

This feature is supported only by insight-mobile. The data to be checked must be downloaded first. Validation overrides existsValidator but can be compensated by findInTree.

existsValidator validates if user input matches a given attribute value of a root node instance in another Insight Config configuration (tree).

  • tree specifies source Insight Config configuration
  • attribute attribute of root node (in the referenced configuration specified in tree)
  • autofill: Array of attributes to be copied if validation succeeds
    • src: name of attribute (in the referenced configuration specified in tree)
    • dst: name of attribute in this node (in the enclosing configuration)
  • filter: Key value mapping to filter the items to select.

Validation

validation provides a set of functions and params to build an interactive input flow (focus, input, validate, warn, error, shift, focus, ...). These params and functions can be used inside a js code snipped which is defined in the js section of the validation.

Trigger:

  • Each change to the corresponding form field will cause the execution of the defined code snipped
  • Each time the form is saved will cause the execution of each defined validation of the enclosing node (see Parameter save)

Params:

  • value represents the actual value
  • attribute JS-Attribute object
  • attributes all JS-Attribute objects of current detail view
  • save Boolean: Indicates whether validation is performed due to saving or due to user input.
  • object server representation of the current object; current changes are not reflected;
  • isNew Boolean: Indicates whether the record is to create
  • userData Properties of the logged in user. (Working with variables/values))
  • nfcResult Read NFC result

Functions:

  • valid: marks the input field as valid: e.g. valid();
  • warn: presents a given warning to the user but does not prevent the data set to be stored, e.g.: warn('Input exceeds max value. Proceed?');
  • invalid: presents a given error message to the user and prevents data from being storde, e.g.: invalid('Not valid ...');
  • attributeForName: retrieves attribute object by its name. e.g.: var attribute = attributeForName('DESCRIPTION');
  • getValueForName: retrieves attribute value by its name (replaces valueForAttributeName). e.g.: var value = getValueForName('DESCRIPTION');
  • next: shifts focus to the next editable field. To shift the focus to the attribute with a particular name call next with parameter, e.g.: next('DESCRIPTION');
  • validAndNext: marks the input field as valid and jumps to the next field; e.g. validAndNext(); or validAndNext('DESCRIPTION');
  • findInTree: Searches in other tree by filter; e.g. findInTree('treename', filterMap).then(callback); The parameter of the callback function is: firstRecords (see code example below) !!! It is absolutely necessary to call valid, invalid or validAndNext in the callback function !!! findInTree can also use a defined search of the destination tree. This only works offline; e.g. findInTree('treename', filterMap, 'searchString, 'nodename', 'searchname').then(callback); The callback function got a second parameter: searchResult (all found rows of the offline search model)
  • selectFromTree: can be called to display the dialog from the validation (insight-mobile only). selectFromTree must be configured at the attribute. searchString, filter and openSingleSearchResult can be used in the options. oneTime can also be configured to use openSingleSearchResult and filter only on the first search. e.g. see below.
  • setValue: Sets the value of the current field; e.g. setValue('Foo');
  • setValueForName: Sets the value of given field; e.g. setValueForName('DESCRIPTION', 'Foo');
  • sound: Plays a sound. There are two default sound files: positive and negative; e.g. sound('positive'); Own sound files can be integrated via static-content. e.g. sound('static-content/my-sound.mp3');
  • remote.validate('middleware/check', {value: value});: calls the remote script with the given data-object and executes valid()/invalid() depending on the success (HTTP-Status 200) or failure (HTTP-Status 500) of the script
  • remote.action('middleware/uuid').then((data) => { setValue(data.uuid); });: calls the remote script with the given data-object, you need to handle the returned object/failure in the method through a promise
  • setReadonly: Sets UI control readonly or not. e.g. setReadonly(true);
  • setReadonlyForName Sets UI control readonly or not for attribute name. e.g. setReadonlyForName('DESCRIPTION', true);
"validation": {
    "js": "value == 10 ? valid() : invalid('Not 10!');"
}
"validation": {
    "js": "if(!save){attributeForName('FR2CODE').value = undefined;} if(value != undefined && value != ''){ next() }"
},
"attributeControl": {
    "js": "attribute.readonly = valueForAttributeName('PROBLEMCODE') == undefined || valueForAttributeName('PROBLEMCODE') == ''; attribute.styleClass = attribute.readonly ? '' : 'bg-color-light-red'"
}
"validation": {
    "js": "findInTree('person', {PERSONID: value}).then(function(firstRecords){ firstRecords.length > 0 ? validAndNext() : invalid('Not found') })"
}
"selectFromTree": {
    "tree": "asset-flat",
    "node": "asset",
    "attribute": "ASSETNUM",
    "autofill": [{
        "src": "LOCATION",
        "dst": "LOCATION"
    }]
},
"validation": {
    "js": "if(!save){var options = { searchString: '*', filter: [{ attribute: 'ASSETNUM', value: assetNum }], openSingleSearchResult: true, oneTime: true, }; selectFromTree(options);}"
}
  • isChanged: Checks whether the attribute value has changed.
"attributeControl": {
    "js": "if(isChanged()){/* do your code */}"
}
  • isChangedForName: Checks whether the value of another attribute has changed.
"attributeControl": {
    "js": "if(isChangedForName('other-attribute-name')){/* do your code */}"
}
  • translate: Translates text with or without parameters e.g. translate('example') or translate('example_with_parameter', {parameter: 'with parameter'});

AttributeControl

attributeControl provides a set of functions and params to set the attribute properties dynamically (see Field validation - validation). The attributeControl-Function can also be used at node configurations.

Trigger:

  • Each change to one of the form fields, having validation or attributeControl, will cause the execution of each defined code snipped
  • On form creation

Parameters and functions are much the same as in the validation section except the next()/validAndNext() functions. These are not accessible during execution of attributeControl.

Params:

  • value: represents the actual value
  • attribute: JS-Attribute object
  • attributes: all JS-Attribute objects of current detail view
  • save: Boolean: Indicates whether validation is performed due to saving or due to user input.
  • object: server representation of the current object; current changes are not reflected;
  • isNew: Boolean: Indicates whether the record is to create
  • userData: Properties of the logged in user. (Working with variables/values))
  • onInit: Boolean: Indicates whether the form is in initialization

Functions:

  • valid: marks the input field as valid: e.g. valid();
  • warn: presents a given warning to the user but does not prevent the data set to be stored, e.g.: warn('Input exceeds max value. Proceed?');
  • invalid: presents a given error message to the user and prevents data from being storde, e.g.: invalid('Not valid ...');
  • validForName: marks the input field as valid: e.g. valid('DESCRIPTION');
  • warnForName: presents a given warning to the user but does not prevent the data set to be stored, e.g.: warn('Input exceeds max value. Proceed?', 'DESCRIPTION');
  • invalidForName presents a given error message to the user and prevents data from being storde, e.g.: invalid('Not valid ...', 'DESCRIPTION');
  • attributeForName retrieves attribute object by its name. e.g.: var attribute = attributeForName('DESCRIPTION');
  • getValueForName retrieves attribute value by its name (replaces valueForAttributeName). e.g.: var value = getValueForName('DESCRIPTION');
  • setValue Sets the value of the current field; e.g. setValue('Foo');
  • setValueForName Sets the value of given field; e.g. setValueForName('DESCRIPTION', 'Foo');
  • sound Plays a sound. There are two default sound files: positive and negative; e.g. sound('positive');. Own sound files can be integrated via static-content. e.g. sound('static-content/my-sound.mp3');
  • remote.validate('middleware/check', {value: value});: calls the remote script with the given data-object and executes valid()/invalid() depending on the success (HTTP-Status 200) or failure (HTTP-Status 500) of the script
  • remote.action('middleware/uuid').then((data) => { setValue(data.uuid); });: calls the remote script with the given data-object, you need to handle the returned object/failure in the method through a promise
  • setReadonly Sets UI control readonly or not. e.g. setReadonly(true);
  • setReadonlyForName Sets UI control readonly or not for attribute name. e.g. setReadonlyForName('DESCRIPTION', true);
  • setReadonlyAll Sets UI control readonly for all attributes or not. e.g. setReadonlyAll(true);
"attributeControl": {
    "js": "attribute.readonly = valueForAttributeName('PROBLEMCODE') == undefined || valueForAttributeName('PROBLEMCODE') == ''; attribute.styleClass = attribute.readonly ? '' : 'bg-color-light-red'"
}
  • calcSelectFromTree: SelectFromTree can be activated for the current attribute.
"attributeControl": {
    "js": "calcSelectFromTree({ tree: 'valuelist-select', node: 'root', attribute: 'valueVarchar' });"
}
  • getParentValueForName: Gets an attribute of the parent object
{
    "attributeControl": {
        "js": "return getParentValueForName('name');"
    }
}
  • isChanged: Checks whether the attribute value has changed.
"attributeControl": {
    "js": "if(isChanged()){/* do your code */}"
}
  • isChangedForName: Checks whether the value of another attribute has changed.
"attributeControl": {
    "js": "if(isChangedForName('other-attribute-name')){/* do your code */}"
}
  • translate Translates text with or without parameters e.g. translate('example') or translate('example_with_parameter', {parameter: 'with parameter'});

Assignment

The assignment function fills the given attribute dynamically with a value when the specified hook is triggered.

  • assignment
    • type: specifies the function to be called
      • text: assigns a value (see chapter Working with variables/values)
    • value: formatted String with variables (see chapter Working with variables/values)
    • when: specifies the trigger
      • afterRead after reading the data
      • beforeWrite: before sending back to the server
      • onSave: save button pressed
"assignment": {
    "type": "text",
    "when": "onSave",
    "value": "Hallo ${WONUM}, ${appParams.standort}, ${userData.personid} - ${func.now}"
}

FormField

An attribute signed as formField creates a field on a detail page which does not refer to an attribute in the backend.

  • Insight Middleware ignores such fields.
  • Insight Client uses these fields like other attributes.
{
    "name": "WORKORDERID_BLAA",
    "formField": true,
    "label": "IDtemp",
    "type": "String"
},
{
    "name": "WORKORDERID_BLAA2",
    "formField": true,
    "label": "IDtemp2",
    "type": "String",
    "assignment": {
        "value": "${DESCRIPTION}",
        "when": "afterRead",
        "type": "text"
    }
},
{
    "name": "DESCRIPTION",
    "label": "Beschreibung",
    "type": "String",
    "assignment": {
        "value": "${WORKORDERID_BLAA}-${WORKORDERID_BLAA2}",
        "when": "onSave",
        "type": "text"
    }
}.

ExcludeExpertSearch

To exclude an attribute from expert search use the following notation:

"excludeExpertSearch": true

Format

Attributes can be formatted with a pattern. The format only affects the display value and the node label.

  • Date:
    • pattern documentation can be found at java SimpleDateFormat
{   
    "name": "REPORTDATE",   
    "label": "Date",    
    "type": "Date",     
    "readonly": true,   
    "format": "pattern:'dd.MM.yyyy HH:mm:ss'"   
}
  • Decimal:
    • pattern documentation can be found at java NumberFormat
{   
    "name": "VALUE",    
    "label": "Decimal", 
    "type": "Decimal",  
    "readonly": true,   
    "format": "pattern:'#0.00',locale:'de'"     
}

Date

Attributes of type date open a special date picker UI when clicked. That datepicker UI is available on web, mobile and electron. Along with the datepicker UI for attributes of type date, additional features are available:

Min / Max

You can specify a valid range in which a user can pick his date. There are several options.

{
    "name": "CREATEDATE",
    "label": "My Date Example",
    "type": "Date",
    "min": [2019, 6, 3],
    "max": [2019, 6, 28]
}

Instead of fixed date boundaries, you may also specify min as an Integer, relative to today. The following example will start the valid pick range 15 days before today.

{
    "name": "CREATEDATE",
    "label": "My Date Example",
    "type": "Date",
    "min": -15,
    "max": [2019, 6, 28]
}

Setting max to true will set the value to today, false removes any limits.

Disabling dates

You are also able to disable specific dates in the datepicker. To disable a list of specific dates, use the following notation:

{
    "name": "CREATEDATE",
    "label": "My Date Example",
    "type": "Date",
    "disable": [[2019, 6, 10], [2019, 6, 11]]
}

If you want to specific weekdays (Mondays, Thursdays,...) you can use the following notation:

{
    "name": "CREATEDATE",
    "label": "My Date Example",
    "type": "Date",
    "disable": [1, 4, 7]
}

Instead of listing all specific dates to disable, you are also able to list the complete range for disabled dates:

{
    "name": "CREATEDATE",
    "label": "My Date Example",
    "type": "Date",
    "disable": [
        { "from": [2016,2,14], "to": [2016,2,27] }
    ]
}

DirtyCheck

Include or exclude from dirty check of detail pages. The dirty check shows a dialog if a user wants to leave a page and attributes are still changed.

  • include: Is used to include attributes with settings like formField.
  • exclude: Is used to exclude attributes with predifined values from settings like attributeControl.
{
    "name": "myfield",
    "type": "String",
    "formField": true,
    "dirtyCheck": "include"
},
{
    "name": "myfield",
    "type": "String",
    "dirtyCheck": "exclude",
    "attributeControl": {
        "js": "setValue('Initial value');"
    }
}

MustWrite

mustWrite Is only working if the default save api is used, no saveAction.

  • true: Attribute is written on save, even if the value is not changed.
  • false: Attribute is never written on save.
{
    "name": "myfield",
    "type": "String",
    "formField": true,
    "mustWrite": "false"
}

Derived Attributes

Derived Attributes do not exist within your EAM, but can be derived from them. Derived attributes are defined in a node's attributes-section together with their normal counterparts. Attribute properties like name, label or section apply as usual. Derived attributes must be of type String and are readonly by nature. A derived attribute is distinguished from an EAM-attribute by its nested derived-element.

"attributes": [
    {
        "label": "mylabel",
        "name": "myname", 
        "derived": {}
    }
]

Derived Date Pattern Attributes

Derived Date Pattern Attributes can be used to format dates in a customized form. They are based on an attribute of type Date. That date attribute must be declared as a sibling EAM attribute. The pattern property defines how the date value is formatted. A Date Pattern Attribute is defined as followed:

{
    "name": "REPORT\_KW",
    "label": "Erstellt KW",
    "type": "String",
    "derived": {
        "pattern": "YYYY/ww",
        "attribute": "REPORTDATE"
    }
}

The pattern's rules are explained here.

Derived Regex Attributes

Derived Regex Attributes can be used to extract parts of other attributes. They are based on an attribute of type String. That string attribute must be declared as a sibling EAM attribute. The regex defines how the resulting value is obtained from the actual attribute's value. With the optional replacement you can empower regular expressions even more. A Regex Attribute is defined as followed:

{
    "name": "location\_part4",
    "label": "Part4 of Location",
    "type": "String",
    "derived": {
    "regex": "([^-]\*-){3}([^-]\*)-",
    "attribute": "LOCATION"
    }
}

The nested properties regex and attribute are mandatory for derived regex attributes. regex denotes a so called extended regular expression as defined e.g. here. attribute denotes the name of the actual EAM attribute. The nested property replacement is optional. Unless given the resulting value is that of the right most group in regex. If the regex does not match, the resulting value will be empty. In the example above the regex attribute's value is derived from EAM's attribute LOCATION. The extracted part is the text between the third and fourth dash. Otherwise, if replacement is given, then the value is the replacement property, where $1, $2 etc are replaced by the captured groups. If the replacement shall contain the dollar-sign or the backslash literally, it must be escaped by Backslash (). Note that JSON-Syntax requires the backslash itself to be escaped by another backslash. Suppose the EAM attribute x has value "abc/pqr/xyz" and the regex attribute is defined like so:

"derived": {
    "regex": "(.\*)/(.\*)/(.\*)",
    "attribute": "x",
    "replacement": "$3 \\$ $1"
}

The derived value would then be xyz $ abc.

Derived Replace Attributes

Derived Replace Attributes can be used to compose values from other values. The replace property defines how the value gets composed. The syntax is the same as for a node's label.

"derived": {
    "replace": "${LASTNAME}, ${FIRSTNAME}"
}