Not another programming language. Part 2: Representation Logic
- Tutorial

The second part of the lsFusion language and platform trilogy. The first part can be found here .
It will focus on the logic of representations, namely, on everything related to combining data and displaying it to the user or other information systems.
It is clear that many may not be so interested in looking at the fighter’s presentation, and they would like to see a real fight, if possible with blood (and it will be, since the discussion of the previous articles helped to better understand the unprotected places of potential competitors and where to beat it) . But here it is necessary to consider two things:
a) it is Habr. That is, a technical resource, they do not like beautiful pictures and advertising slogans here - in order to say something, you need details on how you are going to achieve this.
b) it is a market for the development of information systems, and it is very similar to the market for weight loss products. Here, everyone says that we have a quick and easy. But when it comes to details, in which, as you know, the devil lies, either the simplest CRUDs are used as examples or they resort to various tricks: they show some snippets of code, and hide the main part with the words “it doesn’t matter "," Done in a couple of minutes "and everything like that.
Actually, that’s why we had two options: either start with the advantages and risks of getting reproaches for marketing bullshit, or start with a technical description and the questions “why do we need another language”. Theoretically, of course, all this could be done in one article, but such an article would be difficult not only to read, but even just to scroll through. Accordingly, we chose the second option, although if it’s still important for someone to learn about the reasons for the appearance and advantages of the language right now (and not in future articles), welcome to the site . It consists of only three pages: what, how and why not, and gives, in my opinion, quite enough information to answer all these questions. Plus, there you can try the platform online, including to make sure that there is no “piano in the bushes”, and the code from the examples in this article is really all the code needed to run the application.
But enough lyrical digressions, we return to the
As in the domain logic (first article), all concepts of presentation logic in lsFusion form a stack:

and it is in the order of this stack that I will talk about them.
Table of contents
Forms
A form is the most important (and in fact practically the only) concept in presentation logic, which is responsible for everything - both for user interaction and for printing, exporting and importing data.
The form can logically be divided into two parts:
- The structure of the form determines what data the form shows.
- The presentation of the form determines how it displays this data.
Shape structure
We begin, naturally, with the structure of the form.
The objects
When creating a form, you must first determine which objects it will display. It is worth noting that there may be a slight confusion in terminology between the objects of the form and the objects that are displayed in these form objects. Therefore, in the future, if this is not obvious from the context, we will use the terms “form objects” (for the first case) and “objects in the database” (for the second).
For each form object, you need to set its class. This class can be either primitive (built-in) or object (custom).
FORM currentBalances 'Текущие остатки' |
Each object on the form at any given time has a current value. Its change occurs depending on the presentation, either as a result of the corresponding actions of the user in the interactive presentation, or "virtually" in the process of reading data in a static representation.
Properties and Actions
After defining the objects on the form, you can add properties and actions, substituting the objects described above for them as input to the arguments.
Note that adding actions is relevant only for interactive presentation; they are ignored in reports and exports. Also, given that the behavior of properties and actions from the point of view of their display on the form is exactly the same, in the future we will use only the term property (for actions the behavior is absolutely similar).
Display object
Each property is displayed in exactly one object on the form (we will call it the display object of this property). By default, the display object is an object, the latter for the set of objects that are passed to the input of this property. For example, if we have a form of current balances with two objects - a warehouse and goods, and three properties - names of the warehouse and goods and the balance of goods in the warehouse:
FORM currentBalances 'Текущие остатки' |
However, if necessary, the developer can specify the display object explicitly (that is, for example, in an interactive view show the property with the remainder in the table of warehouses, not goods).
Filters and Sorting
For each form, the developer can set filters and orders that will limit the list of objects available for viewing / selection on the form, as well as the order in which they are displayed.
To set a filter, you must specify a property that will be used as a filter criterion. The filter will be applied to the table of that object, which is the last for the set of objects passed to the input of this property (that is, similarly with the definition of the property display object). In this case, only those sets of objects (series) will be shown for which property values are not NULL. For example, if we add the currentBalance (s, i) OR isActive (i) filter to the form above:
FORM currentBalances 'Текущие остатки' |
Form screenshot

when displaying products, only products that are on the balance or marked as active will be displayed.
Sorts are defined as a list of properties on the form in which order objects should be displayed. Otherwise, everything is similar to filters.
Groups of objects
The platform also has the ability to combine objects into a group of objects . In this case, the “Cartesian product” of these objects will be shown in the tables / lists (that is, for two objects - all pairs, three objects - triples, etc.).
FORM currentBalances 'Текущие остатки' |
Form screenshot

Accordingly, almost everywhere, both before and after, instead of single objects of the form, you can use groups of objects.
Actually, this is what was done in the documentation: the more general term “group of objects” is used everywhere, but so as not to complicate things in this article (and groups of objects consisting of several objects are used much less frequently), it was decided to forget and consider groups of objects, that a group of objects always consists of exactly one object and, accordingly, use the term “object” everywhere instead of the more complex “group of objects” and “set of objects”.
Property Groups
Properties on the form, like objects, can also be combined into groups, which, in turn, are used in the interactive (default design) and hierarchical representations of the form (about them a little later). By default, the binding of a property to a group is global (that is, it is set for a property for all forms at once), however, if necessary, for individual forms this binding can be redefined.
Column Objects
By default, a property is displayed exactly once in its display object. In this case, as the values of objects other than the display object of this property (we call them upper), their current values are used. However, the platform also has the ability to display one property several times so that the values of some top objects are not used by their current values, but all objects in the database that are suitable for filters. With this mapping of properties, a kind of “matrix” is formed - (display object) x (upper objects). Accordingly, in order to create such a matrix, when adding a property to the form, it is necessary to indicate which upper objects should be used to create columns (we will call these objects objects-in-columns).
FORM currentBalances 'Текущие остатки' |
Form screenshot

So, with what the form displays, more or less figured out, let's move on to how it can do this.
Form submissions
There are three form submissions:

View Screenshots
Interactive:

Printed:

Structured:


Printed:

Structured:

- Interactive. A view with which the user can interact is to change data and current objects by triggering various events. Actually, this representation is usually called a form.
- Printed. It is usually called a report - uploading all form data and presenting it in graphical form. Including with the possibility of printing them (from where it got its name).
- Structured - representation of the form in various structured formats (JSON, XML, DBF, etc.). Typically used for further integration with other systems.
Interactive and printed representations are graphic, that is, they display the received data in two-dimensional space: paper or the screen of the device. Accordingly, each of these representations has a design, which, depending on the particular representation, can be set using appropriate mechanisms (about them a little later).
The printed and structured presentation is static, that is, they read all the data at the time the form is opened (as opposed to the interactive one, which reads the data as necessary).
The description of the performances will begin, perhaps, with the most difficult - the interactive presentation.
Interactive presentation
In an interactive view, form objects are displayed in table form. The rows in this table correspond to the objects in the database that satisfy the specified filters, the columns, in turn, correspond to the properties.
However, if necessary, the property can be displayed not as a table column, that is, for all its rows, but as a separate field on the form, that is, only for the current value of the form object. For instance:
currentBalance 'Текущий остаток' (Stock s) = GROUP SUM currentBalance(s, Item i); |
Form screenshot

Changing the current value of a form object occurs either as a result of a user changing the current row of the table, or as a result of performing an action created using a special search operator (SEEK).
Note that the way a property is displayed in a panel or table, as a rule, is set not for each property separately, but as a whole for a form object. Accordingly, if the form object is marked as PANEL, then all its properties are displayed in the panel (that is, for the current value), otherwise (by default) all its properties are displayed in the table. Properties without parameters and default actions are displayed in the panel.
All tables in the interactive view are dynamic by default, that is, only a limited number of objects in the database are read, and the rest are read as the current object in the table changes. The number of displayed objects in this case can either be determined automatically based on the height of the visible part of the table, or set by the developer explicitly when creating the form.
Also, the form in the interactive presentation is completely reactive, that is, it automatically updates all the data on the form when any data that affects them changes (such as React, only in the general case). Plus, all this is done not by full recalculation (as in the same React), but incrementally, moreover, on the SQL server.
In general, it’s funny when, when comparing with other technologies, you try to include the top three requirements in the task, people often make round eyes, as if they were asked to launch a person into space. Although non-fulfillment of the second requirement by any normal user will be classified as a bug, and the first and third requirement is that the developed form will work normally when at least a little data appears in the database (several tens of thousands of records, for example).
Interactive presentation is supported both in web client mode (i.e. web applications in a browser) and in desktop client mode (Java applications). The desktop client, like any native client, has a slightly better responsiveness of the interface, but most importantly, it allows you to work with equipment and perform other operations that are not available in the browser (mainly due to security problems).
Object Trees
In addition to tables, the platform also allows you to organize the display of objects in the form of trees, both flat ("nested" in each other tables) and recursive (for example, "nested" objects in the database).
Flat trees, in fact, are a generalization of tables, when several tables are "combined" into one table at once:
FORM currentBalances 'Текущие остатки' |
Form screenshot

This is a relatively complex mechanism and is rarely used in practice, so we will not dwell on it in detail.
But recursive trees, on the contrary, are used quite often (for example, to implement classifiers). To display a form object in the form of such a tree, it is necessary to set an additional filter for it - a property whose value for the lower objects should be equal to the upper object. Initially, the top object is considered NULL.
parent = DATA ItemGroup (ItemGroup) IN base; |
Form screenshot

User form management
To ensure better ergonomics of the system (including not to create forms for everyone), part of the operations for setting up the interactive presentation of the form can be performed by the users themselves. For example, such operations are:
- setup of tables (visible columns, order, fonts, etc.),
- creating custom filters and sorts,
- grouping data by column values,
- print a table and upload it to Excel.
Also, the developer can create the so-called filter groups, which the user can turn on / off independently. For instance:
EXTEND FORM currentBalances // расширяем ранее созданную форму с остатками |
Note that the functionality described above refers more likely to the functionality of ERP platforms, which is already completely discordant with the title of the article. On the other hand, as mentioned in the first article, in the future, the language / platform claims to be a replacement, including this class of platforms, so it would be wrong to not mention these features at all.
Object Operators
One of the most common scenarios of working with a form is adding / deleting an object, as well as editing it in a new form. To implement such scenarios, the platform has a predefined set of operators that allow you to create the necessary actions in one word right in the form creation operator:
- NEW - create an object
- EDIT - editing an object
- NEWEDIT - creating and editing an object
- DELETE - delete an object
Also, since it is very often necessary to perform these actions in a new session (if you need to separate the actions of creating objects from the actions on the form from which these objects are created), the platform supports the corresponding syntactic sugar - the NEWSESSION and NESTEDSESSION options, which work similarly to the operators of the same name creating actions, but, like the operators themselves working with objects, do not require the developer to create and name new actions. For instance:
FORM teams |
FORM order 'Заказ' |
Shape design
As with most existing GUIs, the design of an interactive presentation of a form is a hierarchy whose nodes are components. Components, in turn, can be:
- containers - components that contain other components.
- basic components - graphical representations of basic elements: tables, property panels, filter groups, etc.
The mechanism for arranging components inside containers essentially repeats CSS Flexible Box Layout (and is implemented in the web client with it), so we won’t dwell on this mechanism in great detail.
Note that the design of the form is usually not created from scratch (since it is quite time-consuming). Typically, a form design is automatically created based on the structure of the form, and then the developer only changes it a little: for example, adds a new container and transfers existing components to it:
Default form design example
The hierarchy of containers and components in the default design will look like this:

FORM myForm 'myForm' |

FORM myForm 'Моя форма' |
Form screenshot

Form Design 2.0 (React)
Looking at the interactive representations of the forms in the above screenshots (or, for example, in the online demo), you can see that the current form design in the user interface is, let's say, quite ascetic. This, of course, has never been a particular problem for information systems, where the main users are employees or partners of the company owning these information systems. Moreover, despite the asceticism, the current form design mechanism allows you to implement very difficult cases, for example, POS:
Form screenshot

But if it comes to, say, SaaS B2B, or even B2C (for example, some Internet banking), then questions immediately begin to appear on how to make the design more ergonomic.
At the current stage, to solve this problem, a special javascript library has been developed , the main task of which is to create and update a special js-object containing form data. Accordingly, this object can be used as state for the React component and thereby create any design and any additional interactivity of the form being developed. For example:
An example of a form on React (on codesandbox)
Or a more complex example - with drop-down lists and using the REST (or rather Stateless) API for this:
An example of a form on React with drop-down lists (on codesandbox)
True, the problem with this approach is that the forms from the examples above will not be embedded either in the general platform interface or in the general control flow of actions. Therefore, one of the most immediate tasks in the development of the platform is the translation of the form design mechanism into the scheme used in the design of the report (printed presentation):
- The platform automatically generates a react design based on the form structure as in the example above.
- If necessary, the developer can save it and edit as he wants. Accordingly, then the platform will use this edited design when opening the form.
Plus, this approach will allow generating React Native forms, and thereby will allow creating native mobile applications. Although this issue (with native mobile applications) we have not worked out very deeply yet.
True, we note that the old design mechanism will also be supported, since, for the same business applications, it perfectly performs its function.
Form Events
Form events are the second key mechanism after domain events, which is responsible for determining when to perform actions.
The platform has a whole set of various form events that arise as a result of certain user actions, but in this article we will consider only one, the most often used of them - the CHANGE event. This event occurs when the user initiated a property change / action call, for example, by pressing any non-system key on the keyboard, being on the field of the property being changed, or by clicking on this field with the mouse.
As for events of the subject area, for events of the form, you can specify processing - the action that will be performed when the specified event occurs. Note that most events of the form already have some default processing that out of the box implements the behavior most expected by the user (for example, with the aforementioned CHANGE event, request input from the user and change the property to the entered value). However, in practice, sometimes situations do arise when for some form event it is necessary to specify some specific processing, for example:
changeQuantity (Order o, Book b) { |
Form statements
To work with a form, the platform supports several special operators that allow, for example, controlling focus, user filters and sorts, the current values of form objects, and so on, but the most important one is probably the aforementioned INPUT value input operator.
For this operator, you can specify a primitive type, the previous value, the action that must be performed if the input was completed successfully, as well as the action that must be performed if the input was canceled. For instance:
FORM order |
- stops the process of performing an action,
- sends a request to the user (in this case, entering the value directly in the field),
- receives an answer from him,
- continues the execution of the stopped action, taking into account the received response.
I must say that INPUT is not the only data entry operator. In addition to it, the dialog form of the message display operator (ASK) is also responsible for data entry:
DELETE Order o WHERE selected(o); |
This concludes with an interactive view and move on to static views.
Static views
As noted earlier, static representations read all form data at the time it was opened. Accordingly, for these representations it is necessary to determine in which order to read this data. For this, form objects must be organized into a hierarchy, within which the data for the objects will sort of “nest” into each other. For example, if we have objects A and B, and A is the parent of B, then in a static view all properties of A for the first object in the database from A will be displayed first, then all properties of B and all properties of the pair (A, B) for all objects in the database from B, then similar information for the second object in the database from A and all objects in the database from B and so on will be displayed (this mechanism for nesting objects into each other is very similar to the already mentioned mechanism of “flat” trees in the interactive representation )
The platform builds the hierarchy of objects automatically based on the structure of the form, although it cannot be said that the full algorithm for constructing this hierarchy is very obvious:
- First, relationships between objects are built according to the following rules:
- After the relationships are built, the hierarchy is constructed in such a way that the parent of the object A selects the latest in the list of objects, the object B, on which A depends (directly or indirectly).
For instance:
FORM myForm 'myForm' |

However, in practice, such complex forms, as in the example above, practically do not occur, and usually the hierarchy of form objects is quite obvious from its structure.
Printed presentation
In order to display the read data in graphical format, the LGPL report generation library, JasperReports, is used.
As with other reporting libraries, in JasperReports each report can contain data groupings, as well as other subreports. Accordingly, the hierarchy of objects is converted into a report as follows:
- “Chains” of objects (that is, O1, O2, O3, ... On, where O2 is the only direct descendant of O1, O3 is the only direct descendant of O2, etc.) are transformed into groups;
- if the object has several descendants, then subreports are created for each such descendant.
If necessary, any chain of objects can be “split” and forcibly created a subreport for the object by setting the SUBREPORT option to this object (usually this is necessary if the object needs to display data even if there is no data in the descendant object):

Example of a form in a print view:
FORM shipment |
Form screenshot

Report design
The already mentioned JasperReports is responsible for the design of the report (in fact, this is its main function in lsFusion). Like a form design, a report design is usually not created from scratch. So, at the first start of the report, the platform automatically creates the necessary files based on the structure of the form, and then the developer can change this design directly from the preview form using the special program JasperSoft Studio.
By the way, the only time we regretted is that the lsFusion plugin is for IDEA and not for Eclipse, because you have to install a separate program to develop the report design (in Eclipse, JasperReports support can be installed as a plugin). On the other hand, IDEA supports such a wonderful thing as language injection, which we used directly inside the jrxml files in which the reports are stored, as a result of which, for example, the search for use and renaming of properties on the form is supported, including in reports, which saved our lives more than once. Well, to be honest, we did not find any GrammarKit analogues with autocomplete in Eclipse (though we had to finish it ourselves), stub indices, support for lazy chameleon nodes (essentially a two-phase parser), and this is all very important for supporting complex languages and most importantly for their effective work on large files and projects. But this is a separate issue.
Structured view
All structured representations (formats) can be conditionally divided into two types:
- Hierarchical (XML, JSON) - one text file, and information for objects is placed in the form of a list (array) inside the information for the parent object.
- Flat (DBF, CSV, XLS) - one table file for each object. At the same time, for each object with a depth in the hierarchy greater than one, a column with the name parent must be present in its table containing the number of the "top" row in the table of the parent object.
Note that working with flat formats with a hierarchy depth greater than one is not very convenient (due to the need to support an additional column), therefore, as a rule, flat formats are used only for working with simple forms (with a hierarchy depth less than one). In other cases, hierarchical formats are usually used. Accordingly, we’ll start with them.
Hierarchical formats
There are two generally recognized hierarchical formats in the world - XML, JSON. Each has its own pros and cons, but in general they do not differ so much from each other, so in this article we will consider only JSON (for XML, everything with more is similar).
If you try to formulate the process of exporting / importing a form in JSON in a nutshell, then everything is quite simple: objects correspond to arrays in a JSON object, property groups to objects, properties to fields. The formal algorithm is a bit more complicated:
Algorithm to convert form to JSON
Before directly proceeding with the export / import of the form, the platform builds a hierarchy of properties, groups of objects / properties as follows:
After the hierarchy is built, the form is exported / imported recursively according to the following rules:
- A hierarchy of objects / groups of properties is constructed in accordance with the hierarchy of objects and objects of display of properties: the group of display of the property is considered the parent of this property, the hierarchy of objects is saved.
- Then for each object X:
- for all descendants of X, the property groups to which they belong are determined, after which these property groups and their ancestors are automatically included in the hierarchy. Wherein:
- the parents of the descendants of X are the property groups to which they belong
- the hierarchy of property groups is maintained
- the parents of the uppermost (that is, without parents) of the used property groups becomes the object X.
- for all descendants of X, the property groups to which they belong are determined, after which these property groups and their ancestors are automatically included in the hierarchy. Wherein:
After the hierarchy is built, the form is exported / imported recursively according to the following rules:
JSON результат ::=
{ JSON с свойствами, группами объектов / свойств без родителей }
JSON с свойствами, группами свойств / объектов ::=
JSON свойства 1 | JSON группы свойств 1 | JSON группы объектов 1
JSON свойства 2 | JSON группы свойств 2 | JSON группы объектов 2
...
JSON свойства M | JSON группы свойств M | JSON группы объектов M
JSON свойства ::=
"имя свойства на форме" : значение свойства
JSON группы свойств ::=
"имя группы свойств" : { JSON с дочерними свойствами, группами свойств / объектов }
JSON группы объектов ::=
"имя группы объектов" : [
{ JSON с дочерними свойствами, группами свойств / объектов 1 },
{ JSON с дочерними свойствами, группами свойств / объектов 2 },
...
{ JSON с дочерними свойствами, группами свойств / объектов N },
]
Export Example:
GROUP money; |
Result
{
"s": [
{
"date": "21.02.19",
"sd": [
{
"money": {
"item": "Товар 3",
"quantity": 1,
"price": 5,
"index": 1
}
}
],
"stock": "Склад 2",
"customer": "Поставщик 2"
},
{
"date": "15.03.19",
"sd": [
{
"money": {
"item": "Товар 1",
"quantity": 1,
"price": 5,
"index": 1
}
},
{
"money": {
"item": "Товар 2",
"quantity": 1,
"price": 10,
"index": 2
}
},
{
"money": {
"item": "Товар 3",
"quantity": 1,
"price": 15,
"index": 3
}
},
{
"money": {
"item": "Товар 4",
"quantity": 1,
"price": 20,
"index": 4
}
},
{
"money": {
"item": "Milk",
"quantity": 1,
"price": 50,
"index": 5
}
}
],
"stock": "Склад 1",
"customer": "Поставщик 3"
},
{
"date": "04.03.19",
"sd": [
{
"money": {
"item": "Товар 1",
"quantity": 2,
"price": 4,
"index": 1
}
},
{
"money": {
"item": "Товар 2",
"quantity": 3,
"price": 4,
"index": 2
}
},
{
"money": {
"item": "Товар 1",
"quantity": 2,
"price": 5,
"index": 3
}
}
],
"stock": "Склад 1",
"customer": "Поставщик 2"
},
{
"date": "04.03.19",
"sd": [
{
"money": {
"item": "Товар 1",
"quantity": 3,
"price": 1,
"index": 1
}
},
{
"money": {
"item": "Товар 2",
"quantity": 2,
"price": 1,
"index": 2
}
}
],
"stock": "Склад 1",
"customer": "Поставщик 2"
},
{
"date": "14.03.19",
"sd": [
{
"money": {
"item": "Товар 2",
"quantity": 1,
"price": 2,
"index": 1
}
}
],
"stock": "Склад 1",
"customer": "Поставщик 2"
},
{
"date": "17.04.19",
"sd": [
{
"money": {
"item": "Товар 2",
"quantity": 5,
"price": 6,
"index": 1
}
},
{
"money": {
"item": "Товар 1",
"quantity": 2,
"price": 6,
"index": 2
}
}
],
"stock": "Склад 1",
"customer": "Поставщик 1"
},
{
"date": "21.02.19",
"sd": [
{
"money": {
"item": "Товар 3",
"quantity": 1,
"price": 22,
"index": 1
}
}
],
"stock": "Склад 2",
"customer": "Поставщик 1"
},
{
"date": "21.02.19",
"sd": [
{
"money": {
"item": "Товар 3",
"quantity": 1,
"price": 22,
"index": 1
}
}
],
"stock": "Склад 2",
"customer": "Поставщик 1"
},
{
"date": "20.02.19",
"sd": [
{
"money": {
"item": "Товар 3",
"quantity": 1,
"price": 22,
"index": 1
}
}
],
"stock": "Склад 2",
"customer": "Поставщик 1"
}
]
}
In general, when working with JSON, a form can be considered a kind of JSON scheme. So, for example, in the IDE, you can generate a form for a given JSON, but opening a form in a structured representation performs the opposite operation - generate a JSON for a given form. The only thing is that while working with structured formats recursive trees are not supported (that is, in fact, JSON objects with dynamic depth), but their support is a matter of time. Otherwise, using the mechanism described above, you can export / import any JSON file.
Flat formats
In flat formats, each file of a form object is a table in which:
- Rows are the objects in the base of this form object.
- Columns are properties whose display objects are equal to this form object.
As noted earlier, flat formats are usually used only for working with simple forms (that is, consisting of one object). Accordingly, in order not to create such forms explicitly, there is a special syntactic sugar for working with them in the platform, which allows you to create these forms directly at the place of export / import:
run() { |
In total, without going into details, we figured out the representations of the forms, there was very little left - to deal with their discovery in these representations.
Form opening
When you open a form for any of its objects, you can pass a value from the call context, which, depending on the view in which the form is opened, will be used as follows:
- In an interactive view, the passed value will be set as the current object.
- In a static view, an additional filter will be installed: the object must be equal to the transmitted value.
run(Genre g) { |
In interactive view (SHOW, DIALOG)
In terms of flow control, there are two modes for opening a form in an interactive view:
- Synchronous (WAIT) - waits until the user closes the form, and only after that, having recorded all the results of the execution, transfers control to the next action.
- Asynchronous (NOWAIT) - transfers control to the next action immediately after opening the form on the client.
By default, the form opens in synchronous mode.
From the point of view of the location of the form, the opened form can be shown in two ways:
- Like a window (FLOAT) - the form is shown as a floating window.
- As a bookmark (DOCKED) - the form opens as a bookmark in the System.forms system window.
By default, in synchronous operation, the form is shown as a window, and in asynchronous mode - as a bookmark.
But perhaps the most important feature of this operator is its so-called interactive mode (DIALOG). In this mode, the operator allows you to return the last current value of a given object (or, if necessary, several objects), and thereby, in fact, enter the value.
Accordingly, just as in the operator of entering values in the field (INPUT), in the dialogue mode of this operator, you can set the initial values of objects (through the mechanism for transferring objects), as well as actions that will be performed if the input was successfully completed (user clicked OK), or, conversely, canceled (user clicked cancel).
FORM booksByGenre |
Printed Presentation (PRINT)
When you open the form in a printed presentation, you can specify a specific graphic (or pseudographic) format into which the generated JasperReports report will be converted: DOC, DOCX, XLS, XLSX, PDF, HTML, RTF and other formats supported by JasperReports. In this case, the resulting file can be either written to the specified property or sent to the user, where he, in turn, can open it using the OS tools (or, more precisely, the program associated with the specified extension).
In addition, in the desktop client, you can open the report in preview mode (PREVIEW), in which the user can independently choose in which format to export this report and / or send it for printing. And finally, you can simply send the report to print without asking the user anything.
В структурированном представлении (EXPORT, IMPORT)
Also, as in a print view, when opening a form in a structured view, you must specify the format in which the form will be exported: XML, JSON, DBF, CSV, XLS, XLSX. The generated file is then written to the specified property.
Also, unlike graphical representations, the operation is supported for a structured representation, the reverse of the opening operation is the import of the form. The form import operator accepts the file (s) in a structured format as an input, then parses it (them) and writes the received data to the properties of the specified form in such a way that when exporting this form back to the imported format, the source file will be received.
Since the import statement is, in essence, the “input statement”, the following restrictions are imposed on the imported form:
- All form objects must be numeric or specific custom classes.
- Properties on the form and filters should be able to change to a given value (that is, as a rule, be primary, although, for example, you can import the value TRUE into the property f (a) = b - in this case, b (f) is written )
Filters are changed to TRUE upon import (to be precise, to the default values of the classes of values of these filters, that is, 0 for numbers, empty lines for strings, etc., but, as a rule, filters still have values of logical types) .
// для импорта примитивных данных, для которых нужно найти объекты в системе |
Navigator
In the simplest version of the user interface, the user opens several fixed forms with which he will work, switching between tabs. However, if there are many forms that the user works with, opening them all at once is not very convenient. In such cases, you can use the so-called navigator to control the work with forms. In this case, when the client starts, the user is shown only the navigator itself (no forms are opened), and the user can open the forms independently as necessary.
In some sense, the navigator is a very specific kind of interactive form submission, adapted for quick and convenient work with the hierarchy of elements in the large window (desktop) mode. Accordingly, the distinguishing feature of the navigator is that it can only display actions without parameters (the most common of which are form openings). Plus, the design of the navigator is also very different from the design of the form.
Navigator Design
In the navigator, all elements are arranged in a hierarchy. In addition to actions, navigator elements can be folders whose main function is to group common functionality in one place.
The design of the navigator itself consists of many windows - desktop components, each of which displays some elements of the navigator. Accordingly, each element of the navigator can be set to which window its descendants should be drawn. Thus, for each window, the set of subtrees of the navigator elements that is displayed in it is uniquely determined.

Each window occupies a predefined section of the desktop.

The entire desktop has dimensions of 100x100 pixels. When creating a window, you must specify the upper left coordinate, the width and height of the window, expressed in points. It is desirable that the windows “cover” the entire area of the desktop. If this does not happen, then the free area will be given to one of the windows (which is not guaranteed). It is also allowed that two windows have absolutely identical coordinates and sizes. In this case, they will be displayed in the same place, but switching between them will go using the tabs.
At any time, in each window there can be one current navigator folder selected by the user. Accordingly, if for a navigator element one of its top hierarchy folders is in another window, and is not selected, then this navigator element is not displayed.
Also in the platform, in addition to all of the above:
- for windows, you can specify the types of graphic components that will be used to display navigator elements in them - toolbar, panel, tree or menu.
- There are several built-in system windows - forms, log, status, root, toolbar, tree, which can be used in various typical tasks (for example, root is usually used to split actions into modules)
FORM items; |
Navigator example

In general, a navigator is a big syntactic sugar that allows you to get a solution ready for use in a few lines out of the box, and such functionality, as well as user form control, is usually present only in ERP platforms. As a result, many people reading an article about the “programming language” may have a logical question: is the navigator in the language? Seriously? Why not in the library? Well, firstly, lsFusion is ideologically language-based (like SQL or ABAP), not a library-based (like Java or 1C) language / platform. Therefore, given that the navigator is not such a domain-specific abstraction and is present in one form or another in all information systems, its inclusion in the language does not seem so illogical. Secondly, to be honest, I never understood than learning a library is easier than learning a syntactic construct. I would even say the opposite: the language interface of communication, in theory, should be more understandable to humans, as it is used in everyday life. But, apparently, many simply have an allergy to new languages, too many of them have divorced recently (even when this was most likely not necessary).
This concludes the presentation logic. In the next article, we will move on to the physical model - everything that is connected:
- with the development of large projects: namespaces, typification, modularity, integration, metacodes, migration and internationalization;
- with optimizing the performance of projects with a lot of data: materialization, tables and indexes.
Summing up all the above, I would like to draw attention to the following three features of the platform:
- One common logic of form for all its representations. This greatly simplifies learning how to develop all the “classic” elements of presentation logic: interactive forms, reports, exports / imports. So, a person, having mastered only one mechanism, for example, interactive forms, can almost immediately engage in the development of reports and integration with external systems.
- Возможность обращения сервера к клиенту. Это позволяет собрать весь control flow в одном месте без этого избыточного разделения логики на сервер и клиент. Впрочем, этому вопросу уделим больше внимания потом – в сравнении с другими технологиями.
- Полная реактивность и работа с данными на SQL сервере (без ORM). Эту особенность мы уже упоминали, когда рассказывали про интерактивное представление, но хотелось обратить внимание на нее еще раз, так как на самом деле эта особенность является очень важной при разработке сложных информационных систем с большим количеством данных.
Заключение
As very often happens, the second parts do not always turn out to be as interesting as the first (although most likely the first was not as interesting, after all, this is a tutorial, and such material, if it needs to be put into a reasonable volume, will, by definition, be rather dry). But, as already mentioned in the introduction, the purpose of this article (as well as the first) was not “selling,” the goal was to enable the reader to create at least a partial picture of what the development of lsFusion is like.
It is also necessary to keep in mind that everything written in this article is far from all the platform features. Many things had to be skipped, some simplified for a better understanding. As a result, most likely, upon completion of the main series of articles, there will be another article “Not Another Programming Language. Behind the scenes. ”, Where I will collect everything that for various reasons did not fall into the first three articles (although in a good way it should have been).
What else I would like to note. According to the experience of the previous article, a significant number of people have the first reaction to many of the claimed things: "This is impossible to implement in the general case." Well and then often followed by a request to tell in a nutshell how exactly we managed to do this. So, the problem is that the implementation of the lsFusion platform is comparable in complexity to the implementation of modern SQL servers. To make all the declared functionality work on large volumes, many optimizers are used, and they work in conjunction, like in an airplane - each upper optimizer insures the lower one if something went wrong, and there are at least six such optimization levels. Moreover, many solve the same problems that, in theory, would have to be solved by SQL-servers themselves (as for example in a recent article) Accordingly, to talk about it all in brief is simply impossible. Of course, we will certainly tell you about all these mechanisms in more detail (including how they can be controlled), but we will do this a bit later, after we finish with a description of the functionality and comparison with other technologies. That is, first we say “What?”, Then “Why not ...?” And only then “How?”.
UPD: The third part of the article can be found here .