The second part of the trilogy about the language and the lsFusion platform. The first part can be found
here .
It will deal with the logic of representations, namely about everything that is associated with the integration of data and display them to the user or other information systems.
It is clear that many may not be so interesting to look at the fighter’s presentation, and they would like to see a real fight, if possible with blood (and it will be good, the discussion of previous articles helped to better understand the unprotected places of potential competitors and where it should be beaten) . But there are two things to consider:
')
a) this is Habr. That is a technical resource, they do not like beautiful pictures and advertising slogans - in order to say something, you need details on how you are going to achieve it.
b) it is a market for the development of information systems, and it is very similar to the market for slimming products. Here everyone declares that we have fast and easy. But when it comes to details in which, as is well known, the devil is hidden, either the simplest CRUDs are cited 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, therefore, we had two options: either to start with the advantages and the risk of getting reproaches in the marketing bullshit, or to begin 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 hard not only to read, but even just to scroll through. Accordingly, we chose the second option, although if it is still important for someone to find out about the reasons for the emergence 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, you can also try the
online platform, 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, back to the
presentation of the fighter description of the logic of representations.
As in the domain logic (the first article), all concepts of representation logic in lsFusion form a stack:
and it is in the order of this stack that I will talk about them.
Forms
A form is the most important (and in fact practically the only) concept in the logic of representations, 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 form structure determines what data the form displays.
- The form view determines how it shows this data.
Form structure
We begin, of course, with the structure of the form.
Objects
When creating a form, you first need to determine which objects it will display. It is worth noting that there may be a little more confusion in terminology between form objects and objects that are displayed in these form objects. Therefore, in the future, if it 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 both primitive (built-in) and object (user).
In accordance with the order of adding objects to the form, an ordered list of objects is formed. Accordingly, the last object for some set of objects will be called an object from this set with the maximum sequence number in this list (that is, as late as possible).
Every 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 with their input as arguments.
Note that adding actions is only relevant for an 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 absolutely 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 object displaying this property). By default, the display object is the object, the last one for the set of objects that are passed to the input of this property. For example, if we have a current balance form with two objects — a warehouse and a commodity, and three properties — the names of the warehouse and the commodity and the remainder of the goods in the warehouse:
Then, for the name of the warehouse, the display object will be s (warehouse), and for the name of the product and the remainder, i (product).
However, if necessary, the developer can set the display object explicitly (that is, for example, in an interactive view, show the property with the remainder in the table of warehouses, and not goods).
Filters and sorting
For each form, the developer can specify 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 filtering criterion. The filter will be applied to the table of the object that 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 (rows) will be shown for which the property values ​​are not equal to NULL. For example, if we add the currentBalance (s, i) OR isActive (i) filter to the form above:
then when displaying goods, only products that are on the balance or are marked as active will be displayed.
Sorts are defined as a list of properties on the form, in which order the objects should be shown. 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.).
Accordingly, groups of objects can be used almost everywhere, both before and after, instead of single objects of the form.
Actually, the documentation has done so: the more general term “group of objects” is used everywhere, but in order 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 about 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, as well as objects, can also be combined into groups, which, in turn, are used in the interactive (default design) and hierarchical views of the form (about them 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, this binding can be redefined for individual forms.
Column objects
By default, a property is displayed exactly once in its display object. At the same time, as the values ​​of objects other than the object of display of this property (let's call them upper), their current values ​​are used. However, it is also possible in the platform to display one property several times in such a way that the values ​​of some upper objects are not their current values, but all the 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, it is necessary, when adding a property to a form, to specify which particular upper objects should be used to create columns (we will call these objects column-in-objects).
So, with the fact that the form displays, more or less figured out, let's move on to how she can do it.
Form submissions
There are three form representations:
Screenshots of viewsInteractive:
Printed:
Structured:
- Interactive. Representation with which the user can interact - change data and current objects by triggering various events. Actually, this representation is usually called form.
- Printed It is usually accepted to call a report — the unloading of all form data and its presentation in graphical form. Including the possibility of printing them (from which it got its name).
- Structured - presentation of a form in various structured formats (JSON, XML, DBF, etc.). Typically used for further integration with other systems.
Interactive and printed representations are graphical, that is, display the data obtained in a two-dimensional space: paper or device screen. Accordingly, each of these representations has a design that, depending on the specific presentation, can be set using the appropriate mechanisms (about them a little later).
The printed and structured presentation is static, that is, all data is read at once at the time of opening the form (as opposed to the interactive, which reads the data as necessary).
The description of representations will begin, perhaps, with the most difficult - interactive presentation.
Interactive presentation
In the interactive view, form objects are displayed in a table. 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 in the form of a column of a table, 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 example:
The change in the current value of the form object occurs either as a result of the 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 of displaying a property in a panel or table, as a rule, is set not for each property individually, but as a whole for the 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 changes in the table. The number of displayed objects can be determined automatically on the basis of the height of the visible part of the table, and the developer explicitly specified when creating the form.
Also, the form in the interactive view is completely reactive, that is, it automatically updates all the data on the form when changing any data that affects them (such React, only in the general case). Plus, all this is done not by full recalculation (as in the same React), but incrementally, and moreover in SQL server.
In general, it’s funny when comparing with other technologies you try to include the top three requirements in a task, people often make round eyes, as if they are asked to launch a person into space. Although the non-fulfillment of the second requirement by any normal user will be classified as a bug, the first and third requirements are that the developed form will work normally when there is at least some data in the database (several tens of thousands of records, for example).
The interactive view is supported both in the web client mode (that is, the web application in the browser) and in the desktop client mode (Java application). The desktop client, like any native client, has a slightly better interface responsiveness, but most importantly, it allows you to work with the equipment, as well as perform other operations that are not available in the browser (mainly due to security problems).
Trees objects
In addition to tables, the platform also allows you to organize the display of objects in the form of trees, as flat (“nested” in each other tables), and recursive (for example, “nested” in each other objects in the database).
Plane trees, in fact, are a generalization of tables, when several tables are “merged” into one table at once:
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 lower objects must be equal to the upper object. Initially it is considered that the top object is NULL.
User form management
In order to ensure the best ergonomics of the system (including not to create forms for each and every), the users themselves can perform part of operations for setting up an interactive form presentation. For example, such operations are:
- table setup (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 so-called filter groups that the user can turn on / off independently. For example:
These are not all possibilities for setting up the system by the user, but we will return to the remaining possibilities in the third article, since most of them still have no direct relation to the logic of ideas.
Note that the functionality described above refers rather to the functionality of ERP platforms, which is completely dissonant with the article title. On the other hand, as mentioned in the first article, in the long term, the language / platform claims to replace, including this class of platforms, so it would be wrong to not mention these features at all.
Object Operators
One of the most frequent scenarios for working with a form is adding / deleting an object, as well as editing it in a new form. To implement such scenarios in the platform, there is a predefined set of operators that allow you to create the necessary actions in one word directly in the form creation operator:
- NEW - object creation
- EDIT - object editing
- NEWEDIT - creating and editing an object
- DELETE - delete 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 corresponding syntactic sugar is supported in the platform - the NEWSESSION and NESTEDSESSION options that work similarly to the eponymous operators creating actions, but, like the operators themselves working with objects, do not require the developer to create and name new actions. For example:
By default, when editing an object, an editing form is invoked, automatically generated for the class of the form object submitted. However, it is often necessary to redefine this form (for example, add additional information, change the design, etc.) In order to do this, it is enough to create the necessary editing form and indicate that it is the default form for editing objects of a given class:
Similarly, the forms for selecting objects of a given class are overridden.
Form Design
As with most existing GUIs, the design of an interactive form view is a hierarchy whose nodes are components. Components, in turn, can be:
- containers - components that contain other components.
- Basic components - graphic representations of basic elements: tables, property panels, filter groups, etc.
The arrangement of the components inside the containers essentially repeats
CSS Flexible Box Layout (and is implemented in the web client using it), so we will not dwell on this mechanism in great detail.
Note that the design of the form is usually not created from scratch (as this is quite laborious). As a rule, the form design is created automatically based on the form structure, and then the developer only slightly changes it: for example, adds a new container, and transfers existing components to it:
Default Form Design ExampleThe hierarchy of containers and components in the default design will look like this:
Form Design 2.0 (React)
Looking at the interactive presentation of forms in the above screenshots (or, for example, in an online demo), you can see that the current design of forms in the user interface, let's say, is very austere. This, of course, has never been a particular problem for information systems, where the main users are employees or partners of the company that owns these information systems. Moreover, despite the asceticism, the current mechanism of form design allows you to implement very difficult cases, for example, POS:
But if it comes to, say, SaaS B2B or even more B2C (for example, some online banking), then immediately begin to appear questions how to make the design more ergonomic.
At the current stage, a special
javascript library has been developed to solve this problem. Its main task is to create and update a special js-object containing form data. Accordingly, this object can be used as a state for React components and thus create any design and any additional interactivity of the developed form. For example:
React form example (codesandbox)
Or a more complex example - with drop-down lists and using the REST (or rather Stateless) API for this:
Example of a form on React with drop-down lists (on codesandbox)
However, the problem with this approach is that the forms from the examples above will not be built into either the general interface of the platform or the general control flow of actions. Therefore, one of the most immediate tasks in the development of the platform is to transfer the form design mechanism to 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 and edit it as he wants. Accordingly, the platform will then use this edited design when opening a form.
Plus, this approach will allow to generate including React Native forms and thereby allow the creation of native mobile applications. Although this issue (with native mobile applications) we have not worked very deeply yet.
However, we note that the old design mechanism will also be supported, since, for the same business applications, it performs its function perfectly.
Form events
Events of the form - the second key mechanism after the events of the subject area, which is responsible for determining the moments when you need to perform actions.
In the platform there is a whole set of various form events resulting from various user actions, but in this article we will consider only one, the most frequently used of them - the CHANGE event. This event occurs when the user initiated a property change / action call, for example, by clicking on any non-system key on the keyboard, being in the field of the property being changed, or clicking on this field with the mouse.
As for domain events, you can specify processing for form events — an action that will be performed when a specified event occurs. Note that most form events already have some default processing that out of the box implements the most expected behavior from the user (for example, for the above-mentioned CHANGE event, request user input and change the property to the entered value). However, in practice sometimes there are still situations when for some event of the form it is necessary to specify some specific processing, for example:
, – , ( , , , paste , ). . .
, , , , , , , , INPUT.
, , , , , , . For example:
, , :
It must be said 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 entering data:
as well as the dialog mode of the operator of the opening of the form (DIALOG) in the interactive presentation, but we return to this mode, as well as to the operator of the opening of the form itself, after we consider the other representations.
With this interactive presentation we finish and move on to static views.
Static views
, . , , . , «» . , A B, A B, A A, B (A, B) B, A B ( «» ).
, , :
For example:
, , , , .
LGPL – JasperReports.
, JasperReports , . , :
- «» ( , O1, O2, O3,… On, O2 – O1, O3 – O2 ..) ;
- , .
«» , SUBREPORT ( , -):
:
JasperReports (, lsFusion). , . , JasperSoft Studio.
, lsFusion- IDEA, Eclipse, ( Eclipse JasperReports ). IDEA , language injection, jrxml-, , , , , , . , , Eclipse GrammarKit autocomplete (, ), stub-, lazy chameleon- ( ), , . .
Structured presentation
All structured representations (formats) can be divided into two types:
- Hierarchical (XML, JSON) is 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. In addition, for each object with a depth in the hierarchy greater than one, its table should contain a column named parent, 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 will begin with them.
Hierarchical formats
– XML, JSON. , , JSON ( XML ).
/ JSON , : JSON-, – , – . :
JSON/ , / :
, / :
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 }, ]
:
{ "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" } ] }
, JSON JSON-. , , IDE JSON , – JSON. , ( , , JSON- ), . / JSON .
, :
, , , ( ). , , , / :
, SELECT SQL. , , , ( , ).
, , , – .
, , , :
– ( ) . .
(SHOW, DIALOG)
:
- (WAIT) – , , , , .
- (NOWAIT) – .
.
, :
- (FLOAT) – .
- (DOCKED) – System.forms.
, – .
, , (DIALOG). (, , ), , , .
, , (INPUT), ( ), , , ( ), , , ( ).
(PRINT)
( ) , JasperReports : DOC, DOCX, XLS, XLSX, PDF, HTML, RTF , JasperReports. , , , , ( , ).
, - (PREVIEW), , / . , , .
(EXPORT, IMPORT)
, , , : XML, JSON, DBF, CSV, XLS, XLSX. .
, , , – . () , () , .
– , , « », :
- .
- ( , , , , , TRUE f(a) = b – f(a) b)
TRUE ( , , 0 , .., , , ).
Navigator
, , . , , , . . ( ), .
In some sense, the navigator is a very specific kind of interactive form presentation, adapted for fast and convenient work with the hierarchy of elements in the large window (desktop) mode. Accordingly, a distinctive feature of the navigator is that it can display only actions without parameters (the most frequent of which are form openings). Plus the design of the navigator is also very different from the form design.
Navigator Design
. , — .
– , . , , . , , .
.
100x100 . , , . , «» . , ( ). , . , .
. , - , , .
:
- , – , , .
- – forms, log, status, root, toolbar, tree, (, root, , )
, – , , , , , ERP-. , , « », : ? Seriously? ? , -, lsFusion language-based ( SQL ABAP), library-based ( Java 1C) / . , , – domain-specific , . -, , , , . : , , , . , , , ( ).
. – , :
, :
- . «» : , , / . , , , , , , .
- . control flow . , – .
- SQL ( ORM). , , , .
Conclusion
As is often the case, the second parts do not always turn out as interesting as the first ones (although, most likely, the first part was not so interesting, after all, this is a tutorial, and such material, if you need to fit it in a reasonable amount, will by definition rather dry). But, as mentioned in the introduction, the purpose of this article (like the first one) was not “selling”; the goal was to give the reader an opportunity to make at least a partial picture of what development on lsFusion is.
, , – . , . , , « . .», , ( - ).
. : « ». , . , , lsFusion SQL-. , , , – , - , . , SQL- (
). , . , , ( , ), , , . «?», « ...?» «?».