Skip to main content
Version: 7.0

Interactive view

A form opened in interactive mode is a graphical component with a certain design in which the user can trigger various events and thereby navigate through system objects, view and change property values, execute actions, and so on. Developers can also use an additional set of operators with this view, making it possible to manage the open form.

Object views

In the interactive view, object groups can be displayed in a table. The rows in the table are object collections, and the columns are properties. The records displayed in the table and their order are determined by the current filters and orders.

Current values of objects can change either as a result of an action created using the special search operator (ACTIVATE), or as a result of a change to the current row, if an object group is displayed in a table.

When an object group is displayed in a table, the number of rows (object collections) displayed can either be determined automatically based on the height of the visible part of the table, or specified by the developer explicitly when creating the form. In the automatic case, the size of the first batch of rows read is set by the working parameter pageSizeDefaultValue (50 by default); the client sizes the batches that follow itself, to the height of the table.

The button that exports a table to XLSX has a fallback for a result that would be too large. The working parameter minSizeForReportExportToCSV turns it on: it is -1 by default and there is no fallback, while at zero or above the platform compares the size of the collected data in bytes with it and, if the data is larger, returns the same data as a CSV file with ; as the separator instead of XLSX.

On the web client, the leading columns of a table stay pinned (sticky) while it is scrolled horizontally, as long as the cumulative width of the columns up to and including a given one stays within the fraction of the visible table width set by the working parameter maxStickyLeft (0.33 by default); the first column whose right edge would cross that fraction, and every column after it, scrolls normally.

Object trees

The platform also allows to display multiple object groups in one table simultaneously. This happens similarly to the object group hierarchy in a static view, i.e. if we have two groups A and B then, in the "joined" table, the first object collection from A is displayed first, then all object collections from B (as filtered), then a second object collection from A, then again all the object collections from B and so on. In this case, it is highly desirable that the filters for B used all objects from A, since otherwise combining these groups into a single tree doesn't make sense. Initially, when a form is opened in the table, only objects of the topmost object group are displayed, but at the same time, a special column is created on the left of the table, using which the user can open nodes on his own and thus view only objects of interest in the lower object groups. Another function of this created column is to demonstrate the nesting of nodes by tabulating the elements inside this column (this allows the user to better understand what level of the hierarchy he is currently at).

Object trees also can be used to display hierarchical data (such as classifiers). In this case, the descendants of the object collection of a group in the tree can be not only object collections of lower groups but also object collections of the same group (such an object group shall be called hierarchical). To determine these child object collections in a hierarchical object group, it is necessary to define an additional filter for it – which, unlike regular filters, can refer not only to the values of the filtered object collections but also to the values of the "upper in the tree" object collection (the same approach is used in the recursion operator). It is highly desirable that the hierarchical filter uses all the values of the upper object collections, since otherwise, as with filters between different groups of objects, creating such a tree doesn't make sense. Initially, it is assumed that all values of the "upper in the tree" object collection are NULL.

info

In the current platform implementation, hierarchical groups allow only trees to be displayed (not directed graphs). Accordingly, it is allowed to use only values of the upper object collections and properties that take lower (filtered) values of objects as input for a hierarchical filter (so that it is guaranteed that the same tree node cannot be reached in different ways)

The properties of different object groups in the tree are arranged in columns under each other, that is, the first column displays the first properties of each object group, the second column displays the second ones, and so on. The total number of tree columns is determined by the last group of objects on the tree (all "extra" properties of the upper groups are simply ignored).

Property views

Any property or action can be displayed on a form in one of the following views:

  • Panel (PANEL): a separate component that displays a property caption and this property value for the current values of the form objects.
  • Toolbar (TOOLBAR): similar to a panel, but this component has a different default location (immediately below the table), and if the table to which a toolbar belongs is hidden then the toolbar is hidden with it.
  • Popup (POPUP): similar to a panel, but placed inside a collapsed popup that the user opens with a separate button; suitable for properties that only need to be shown on demand.
  • table column (GRID): a separate column in the table that displays the property values for all object collections (rows) in the table.

For each object group, you can specify which default view the properties of this group will be displayed in (by default, this view is a table column). If the property has no parameters (that is, it does not have a display group), it is displayed in a panel. Actions are always displayed in a panel by default.

info

For the remainder of the section, the behavior of properties and actions is exactly the same and so we will use only the term property (behavior is absolutely identical for actions).

If necessary, the developer can explicitly specify which view a property should use.

If at any point there are no properties displayed in the table for the object group, the table is automatically hidden.

By default, the caption of each property on the form is the title of the property itself. If necessary, the developer can specify a different caption, or, if you need even more flexibility, use a property as a caption. This caption property can receive upper objects of the displayed property as input. It is also worth noting that if groups-in-columns are defined for the property, then it is desirable to have different captions for the created columns (in order to distinguish them somehow): in this case, it is recommended to use a property that receives all (!) objects of the defined group-in-columns as input.

In addition to the captions, you can define colors (both the background color and the text color) for each property view on a form, as well as a condition that needs to be met for the property to be displayed. Like the caption, each of these parameters is defined using some property.

A property whose value is an object of a user class displays only the internal identifier of that object — a number that tells the user nothing. So instead of the object link itself, a form usually displays a composition through it — for example, the caption of the linked object; a write through such a composition passes into the link, so when editing, the user chooses the linked object.

If such a composition through a link can be changed, and its outer part — the view of the options, for example the caption of the linked object — is unique among the matching objects, the web client shows the value as a selection element: the list of all the objects the choice dialog would offer, displayed by that view; the user picks one of them, and the picked object is written into the link. A stored property whose own name reads, in camelCase, as name, id, number or caption is presumed unique outright, without consulting any statistics — this covers a typical composition like the caption example above. For any other property, whether the options are unique is trusted only above minInterfaceStatForValueUnique matching objects (100 by default): at that count or below, the platform doesn't trust its own row-count statistics enough to answer either way, and no selection element is built. Above that count, the kind of element is determined by the working parameters: if the total length of the views of all options does not exceed maxLengthForValueButton (40 by default), and in a table column maxLengthForValueButtonGrid (10 by default), a button group is drawn — adjacent toggle buttons with the picked option highlighted; otherwise, outside a table column, with at most maxInterfaceStatForValueList (3 by default) options — a list, a column of radio buttons (in a horizontal container, the same button group); otherwise, with at most maxInterfaceStatForValueDropdown (20 by default) options — a dropdown, a standard drop-down list (a select element) that shows the picked option and opens the others on click, without text entry; with more options no selection element is built, and ordinary editing is used. If the property can be NULL, the element also allows clearing the value: clicking the picked option again (in a dropdown, an empty option) writes NULL. For a property shown read-only, no selection element is built automatically. The kind of element can also be set explicitly or disabled in the form design.

When focus moves into a text input on a form, the platform can start changing that property right away, without waiting for the user to type. suppressOnFocusChange (false by default) is what allows it: with the parameter off the change never starts on focus alone, and with it on the change starts, except when the focus was moved by the platform itself - a mouse click, scrolling, arrow-key navigation, restoring focus, applying a filter, and so on - where it is always suppressed.

The default actions of a property whose value is an object - editing that object, or choosing another one in a dialog - are built for any such property whose view can be changed; the working parameter onlyUniqueObjectEvents (false by default) restricts them to the properties whose view is unique, which can leave a great many properties without them. These actions are built and kept as the forms are prepared, so a change of the parameter takes effect only after a server restart.

A selection of several values is built for a grouping that concatenates strings (CONCAT) when the concatenated view is taken under a condition and that condition is a single changeable logical property of the grouped object and the result's parameters, most often a data one: in = DATA BOOLEAN (Book, Tag) and tags(Book b) = GROUP CONCAT name(Tag t) IF in(b, t), ', ' ORDER name(t), t (the condition can also be moved into the selection of the group's elements: GROUP CONCAT name(Tag t), ', ' ORDER name(t), t WHERE in(b, t)). The web client shows such a property not as a string but as a selection element over the objects whose view is not NULL; the user checks and unchecks options, each toggle writes TRUE or NULL into the condition for that object, and the concatenated string is merely recomputed. The condition may also be a composition with a write path (in(b, t) AND active(t) — the write goes into in), but not a chain of several conditions (name(t) IF active(t) IF in(b, t)) and not a property without a write path; a concatenation of only a part of the group (TOP) is not shown as a selection element. The kind of element is determined by the same working parameters and statistics as for a single selection: if the total length of the views does not exceed maxLengthForValueButton (in a table column, maxLengthForValueButtonGrid) — separate toggle buttons; otherwise, outside a table column, with at most maxInterfaceStatForValueList options — a column of check boxes (in a horizontal container, the same buttons); otherwise, with at most maxInterfaceStatForValueDropdown options — a dropdown in which several values are checked; with more options — a text field with a drop-down list: the options are not loaded whole but looked up by the typed text. The set of options is defined by the concatenated view itself, so to offer not all objects of the class, the view is taken under an additional condition in its own parentheses: GROUP CONCAT (name(Tag t) IF active(t)) IF in(b, t), ', ' ORDER name(t), t — only the matching objects are offered and concatenated (an already checked object that no longer matches stays checked in the element, but without a caption). An explicitly specified change action replaces the built-in editing: no selection of several values is built for such a property.

On the desktop client, showNotDefinedStrings (false by default) marks a genuinely absent (NULL) string-typed value with an explicit placeholder instead of a blank cell: "Not defined", or "Required" if the property must be filled in. An empty string value goes the other way: with the setting off, it is marked as "Empty string" so it isn't mistaken for a blank cell; turning the setting on renders it as a literal blank instead.

Filter group

In order to provide the user with an interface for choosing filters to apply, they can be combined into filter groups. For each of these groups, a special component will be created on the form: the user can use it to select one filter from the group as the current active filter. If several filters in one group are applied to different object groups, then the component will be displayed for the last of them. Besides the specified filters, the component by default contains the (All) item, allowing the user not to apply any of the group's filters.

The developer can specify a name for each filter group which can be used to access it in the future (for example, in form design).

Custom filters/orders

The user can change existing orders or add their own, as well as add their own filters using the corresponding interfaces:

  • Orders – by double-clicking on the column heading.
  • Filters – by using the corresponding button under the table for each object group. By default, the filter is set to the active property in the table, and filters it for equality to the entered value (for all types except case-insensitive string types, where the filter is set to include the entered string). If necessary, the developer can specify the default filtering type explicitly by using the corresponding option.

When several filters are chained together with AND/OR, by default the junction between two conditions is drawn as a separator glyph (a vertical bar); the working parameter useTextAsFilterSeparator draws it as a clickable "AND"/"OR" text label instead, which also switches the junction when clicked.

At any one time all filters currently active on an object group — the permanent ones, the user-added ones, and view filters — are combined into a single cached condition before being applied, so that changing one of them doesn't force the platform to recompute every other one from scratch; the working parameter disableCombineFilters turns this off and evaluates/caches every filter independently instead, as a compatibility fallback. The result is identical either way.

Async input value list

Typing into a property's input list (declared with the INPUT ... LIST operator), a filter's value field, or a "type to search" completion field, runs a query against the database on every keystroke rather than filtering an already-loaded list; a family of working parameters tunes how that query is built and how many rows it may read.

The typed text matches candidates by full-text MATCH by default; inputListSearchInsteadOfContains set to false switches it to a plain substring CONTAINS instead. In MATCH mode, the character that separates several search terms typed into one field is matchSearchSeparator (, by default); the client also shows it in the field's search hint and escapes it if the user types it literally. With no explicit ORDER on the input list, results are read by an incremental-LIMIT strategy that stops as soon as enough are found; inputListOrderByRank switches that off and instead runs one query that always ranks candidates by full-text relevance.

The incremental strategy itself is shaped by: asyncValuesNeededCount (15 by default), how many suggestions the dropdown wants; asyncValuesMaxReadCount (1000), how far that estimate may grow before the platform gives up on limiting the read and runs the query unrestricted; and asyncValuesExtraReadCoeff (1.5), the over-read multiplier applied to the estimated hit rate, and again, geometrically, to grow the LIMIT on each retry if too few distinct results came back. If the underlying relation is estimated to have more rows than asyncValuesMaxReadOrderCount (1000), the query drops ORDER BY entirely for performance. This input list is offered automatically once a property's candidate list would cost fewer than asyncValuesMaxReadDataCompletionCount rows to build (100000 by default) — except for a text/JSON/XML-typed property, which is left out of this automatic behavior and needs an explicit inputlist annotation to get one regardless of cost; at that cost or past it, every property needs the annotation. A noinputlist annotation always turns the input list off. On a relation with an estimated size at or above asyncValuesTooShortDataCompletionCount rows (100000), typing asyncValuesTooShortThreshold characters or fewer (-1 by default, meaning this guard is off) returns "String too short to search" instead of running the query, protecting large tables from a near-full scan on every keystroke.

Query results are cached per property; a typed prefix of at least asyncValuesLongCacheThreshold characters (4 by default), or an input list that already carries fixed values, is cached separately from shorter, more generic (and so more repeated) queries. When the user keeps typing while a previous request for the same field is still running, the platform interrupts that request's thread by default to free the resources it holds; disableAsyncValuesInterrupt turns this off for JDBC drivers that don't tolerate being interrupted mid-query. For a filter's own value completion, useGroupFiltersInAsyncFilterCompletion decides whether the object group's other currently active filters additionally narrow the candidates: 0 never combines them in, 1 combines them but the platform may still optimize the combination away, 2 (default) always combines them.

Default objects selection

In the interactive form view, object group filters can change as a result of various user actions (for example, changing the upper objects of these filters, selecting filters in the filter group, etc.), after which the current objects may no longer meet the conditions of the new filters. Also, when a form is opened, some objects may not be passed or may be passed equal to NULL. In both of these cases, it is necessary to change the current objects, to some current default objects. The platform provides several options for selecting new current objects:

  • First (FIRST) - the first object collection (in accordance with the current order)
  • Last (LAST) – last object collection.
  • Previous (PREV) – the previous object collection (or as close to it as possible).
  • Undefined (NULL) – NULL values collection.

If none of these options is explicitly specified, the platform will try to determine whether the permanent filters in the group of objects are a) mutually exclusive for different values of the upper objects (if any), and/or b) the filter selects a very small percentage of the total number of objects of the specified classes. In both of these cases, it makes no sense to search for the previous object and, by default, the first object is selected (FIRST); in all other cases, the previous object (PREV).

For an object of a built-in class in a panel, whose values are not restricted by the filters of the object group, for any default objects type other than NULL, if the object value is not passed when the form is opened and it has no previous value kept by the PREV type, the current value is set to the default value of its class: for example, an object of the DATE class is initialized with the current date when the form is opened. To leave such an object equal to NULL, the NULL default objects type is set for the object group.

info

It is worth noting that the selection of objects by default is pretty the same as the object search operation, where the search objects are:

  • for type PREV
    • on opening a form: either the passed objects, or, if there are none, the last used objects for the form object class.
    • in other cases: the previous current object values
  • for other types
    • on opening the form - passed objects
    • in other cases – an empty object collection

Search direction is determined by the object's default type (PREV here is equivalent to FIRST).

Object operators

When adding properties to a form, you can use a predefined set of operators that implement the most common scenarios for working with objects instead of using specific properties (thus avoiding the need to create and name these properties outside the form each time):

  • Object value (VALUE) – for a form object of built-in class , a special property with one argument will be added which displays the current object value and allows the user to change it. For custom classes, a property will be added which displays the object ID in the database; when you try to change it, it shows a dialog with a list of objects of that class. The selected value will be used as the current value of the object on the form.
  • Object interval (INTERVAL) – for a pair of form objects of the same date or time class, a single property is added which displays the interval between them and, when both objects are displayed in a panel, lets the user edit it; when the interval is changed, the first object becomes its lower bound and the second object its upper bound.
  • Create object (NEW) – adds an action without arguments, which creates an object of the class of the passed form object (or the class explicitly specified by the developer), after which it automatically makes this object current. If the class has descendants, the user will be shown a dialog where he can select specific child class. If any filters are applied to the form object, for which the object is created, the system will try to change the newly created object's properties so that it meets these filter conditions (as a rule, for created objects, the default value of the class of each filter's value is written to that filter)
  • Edit object (EDIT) – adds an action with one argument, which calls the System.formEdit action (which, in turn, open the default edit form for the edited object class).
  • Create and edit an object (NEWEDIT) – adds an action without arguments which creates an object of the form object class, calls the edit object action (EDIT), and if the input is not canceled, sets the added object as current.
  • Delete object (DELETE) – adds an action with one argument which deletes the current object.

You can also specify options for the last four operators (ignored for all other actions):

  • New Session (NEWSESSION) – in this case, the action added to the form will be executed in a new session. When opening forms in a new session, it is important to remember that changes made in the current session (form) will not be visible. Thus, this mechanism is only recommended if the form is opened from a form in which the user cannot change anything, or if the properties and actions of the two forms do not intersect in any way. Note that when the operator is used to create a new object (NEW) in a new session, the object is not only created but also edited (NEWEDIT) (otherwise, the session would immediately close and your changes would be lost).
  • Nested Session (NESTEDSESSION) – the action will be executed in a new nested session. As with a new session, NEW is replaced by NEWEDIT.

Selection/editing forms

For each form, you can specify that it is the default form for viewing/editing objects of a given class. In this case, this form will be opened when you call actions created using the operators for object operations (create/edit an object). The same form will be opened when the corresponding form selection option is used in the form opening operator.

If list/edit form is not defined for a class, the platform will create one automatically. This form will consist of one object of the class, along with all properties matching the class and belonging to the System.base property group. Also, actions of creating, editing and deleting an object in a new session will be automatically added to the form, along with the object value property if there are no properties from the System.id property group corresponding to the class of the object (that is, no "ID" of the object has been added to the form).

Session owner

Since a form is opened by default in the current session, it may not always be safe to apply/cancel changes to this session: for example, the changes made in other forms may accidentally be applied. To avoid such situations, the platform has the concept of a session owner – a form which is responsible for managing the life cycle of the session (for example, applying / canceling changes). By default, it is considered that a form is the session owner if the session did not have any other owner when the form was opened.

To implement the mechanism for working with session owners the platform uses a numerical local property called System.sessionOwners. Accordingly, this property is incremented by 1 when you open a form and decremented by 1 when you close it. Thus, it shows the nesting depth of the "form opening stack", and is NULL if the session has no owner and not NULL otherwise.

If necessary, the developer can explicitly specify when opening a form that this form is the owner of the session that it uses.

info

Session ownership only affects the display / behavior of system actions for managing the life cycle of a form / session. When using the remaining actions, it is recommended that the developer should consider the risk of applying the "wrong" changes by himself (and, for example, use the mentioned above System.sessionOwners property).

System actions for form/session lifecycle management

The following system actions are automatically added to any form (their names are specified in brackets):

  • Refresh (System.formRefresh) - updates the current state of the form, re-reading all the information from the database.
  • Save (System.formApply) - saves the changes made on the form to the database.
  • Cancel (System.formCancel) - cancels all changes made on the form.
  • OK (System.formOk) – closes the current form and, if the form is the session owner, applies the changes to the database.
  • Close (System.formClose) - closes the current form and does nothing with the changes.
  • Drop (System.formDrop) – closes the current form and returns NULL as the selected object.

By default, these system actions have the following visibility conditions:

ActionCondition
RefreshAlways
Save, CancelIf the form is the owner and actions that change the current session can be called on the form. Cancel may not be shown if the platform determines that canceling the changes is guaranteed to lead to a change of the initial values of form objects (i.e., selecting other objects)
OK, CloseIf the form was opened synchronously
DropIf the form is opened synchronously, returns a value and allows NULL values to be passed

If necessary, all these actions can be shown/hidden by removing the corresponding components from the form design and/or using the corresponding options in the open form operator.

Busy indicator

While the client waits for a synchronous request to the server to return — applying changes, running an action, and so on — nothing tells the user a request is in flight until about the delay set by the working parameter busyDialogTimeout (1000 ms by default) has passed, after which a busy indicator is shown; a lower configured value is not honored exactly — the desktop client never waits less than 1000 ms, and the web client subtracts about 500 ms of its own delay from it, down to a floor of 500 ms. The web client always shows a modal dialog; on the desktop client, busyDialog chooses between that same dialog — with a Cancel button and a periodically updated progress message (the default) — and, if turned off, a plain wait cursor with no way to cancel. On the desktop client, maxRequestQueueSize (0, meaning unlimited, by default) additionally caps how many of the client's own fire-and-forget asynchronous requests may be queued at once; once the backlog reaches that size, further user-triggered requests are sent synchronously (and so become subject to the busy indicator above) instead of piling up, protecting the connection from being flooded by rapid repeated actions such as fast cell edits.

On the web client, a server log message that arrives while the Logs panel isn't the focused one is announced with a transient floating notification by default; enableShowingRecentlyLogMessages makes the panel briefly reveal itself instead. On the desktop client nothing marks the new message by default; the same setting makes its own Logs panel do that same brief reveal.

Additional features

You can specify an image file which will be displayed as the form's icon.

Also, the form can refresh itself automatically at a specified interval, by scheduling the System.formRefresh action to run periodically as a form event.

Hovering over a table column header, on either client, or over any other UI element on the web client, shows a detailed-info tooltip after the dwell time set by the working parameter tooltipDelay (1500 ms by default; 0 disables it). On the desktop client, a non-header element still waits a fixed 1500 ms regardless of the configured value — tooltipDelay there only decides whether it gets a tooltip at all, not how long the wait is. On the web client, the viewport is bucketed by its pixel area (width × height) into one of three display-size tiers that drive compact/responsive styling across the UI, with the boundaries between them set by two working parameters: tiny at or below maxPixelTinySize (748800, e.g. 1280×585), mini at or below maxPixelMiniSize (1121280, e.g. 1536×730), and normal above that.

Language

All of the above options, as well as defining the form structure, can be done using the FORM statement.

Open form

To display the form in the interactive view, the corresponding open form operator is used in interactive view.

Examples

date = DATA DATE (Order);
FORM showForm
OBJECTS dateFrom = DATE, dateTo = DATE PANEL
PROPERTIES VALUE(dateFrom), VALUE(dateTo)

OBJECTS o = Order
FILTERS date(o) >= dateFrom, date(o) <= dateTo
;

testShow () {
SHOW showForm OBJECTS dateFrom = 2010_01_01, dateTo = 2010_12_31;

NEWSESSION {
NEW s = Sku {
SHOW sku OBJECTS s = s FLOAT;
}
}
}
FORM selectSku
OBJECTS s = Sku
PROPERTIES(s) id
;

testDialog {
DIALOG selectSku OBJECTS s INPUT DO {
MESSAGE 'Selected sku : ' + id(s);
}
}

sku = DATA Sku (OrderDetail);
idSku (OrderDetail d) = id(sku(d));

changeSku (OrderDetail d) {
DIALOG selectSku OBJECTS s = sku(d) CHANGE;

//equivalent to the first option
DIALOG selectSku OBJECTS s = sku(d) INPUT NULL CONSTRAINTFILTER DO {
sku(d) <- s;
}
}