Lyra provides a quick way to develop user interface forms linked to Celesta cursors. Cursors can be filtered and sorted.
In order to use lyra, one should add the following dependency to their project:
<dependency>
<groupId>ru.curs</groupId>
<artifactId>lyra</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
Lyra Form
Lyra forms can be of two types:
Card and Grid.
Each form is defined by a class inherited from the appropriate base class (BasicCardForm
or BasicGridForm
).
The form itself is set declaratively and is characterized by:
-
Cursor, to which the form is linked. Each of the Lyra forms must override the
getCursor (CallContext context)
method, which returns the corresponding Celesta cursor. If necessary, in the same method, filters and sorting can be applied to the cursor. -
set of bound and unbound fields displayed on the form.
Lyra takes on the task of transferring information between the form and the server, as well as navigating through the table records. The grid form also by itself solves the problem of fast display, scrolling and positioning of the grid with a large number of records. For the developer it is sufficient only to declare the fields they would like to have on the form, with order and properties.
Field Types
Fields, as already mentioned, can be of two types:
-
bound with the cursor field (i.e. column of the table), the values of the bound fields are stored in the database automatically,
-
unbound, that is, not linked with any of the fields of the cursor, but with a getter/setter method of the form class.
When the user edits the bound fields, their new values are written into the fields of the cursor and stored in the database automatically. The values of the unbound fields are passed as parameters to the appropriate methods, and Java code executed on the server can use these values.
Designing a Form from Scratch in Five Steps
To create a Lyra form from scratch, you must follow the following steps:
Step 1. Create a class inherited from BasicCardForm
or BasicGridForm
, optionally annotated with @LyraForm
:
class TestForm extends BasicCardForm{
....
}
or
@LyraForm(gridwidth="600px",
gridheight="200px")
class TestForm extends BasicGridForm{
....
}
If you add FormInstantiationParameters
argument to the form’s constructor, the respective object will be passed to the constructor automatically.
You may also define
FormInstantiationParameters
field annotated with @FormParams
annotation, and its value will be automatically injected.
Step 2.
Override getCursor(CallContext context)
method so it returns sorted and filtered cursor.
You may use
FormInstantiationParameters
object defined in the previous step to define sorting and filtering.
This cursor is going to be the source of the record set for the form:
TestCursor getCursor(CallContext context){
TestCursor result = new TestCursor(context);
result.setRange("myField", myFilterValue);
return result;
}
Step 3 (optional). If you need unbounded fields on the form, you should declare them as getters and, optionally, setters, annotated with @FormField
.
Getters / setters should follow the Java getter/setter names convention:
@FormField(celestatype="INT",
caption="Подпись поля",
width=30)
public int getMyField() {
return this.my;
}
public void setMyField(int value) {
this.my = value;
}
Step 3 (optional). You may use CelestaSQL’s CelestaDoc to set the bound field’s properties. You may omit this step as well: first, Lyra will choose reasonable default values (e.g. table field name for caption, as needed in most cases); second, all the properties definitions can be set in the form’s class itself.
create table test (
/**
{"caption": "Identifier"}
*/
id int not null default seq primary key,
/**
{"caption": "Integer Value"}
*/
attrInt int default 3
);
Step 5. In the form class constructor, define the set and order of form fields by calling the following methods:
-
LyraFormField createField(String name)
adds a field with the given name to the form and returns an object of typeLyraFormField
. The name value must match-
either one of the column names of the form cursor (this creates a bound field),
-
or with the name of the property of the form class declared with
@FormField
annotation, this creates and unbound field. The respective methods must be named according to common convention, e. g.int getSomething(..)
for integer property namedsomething
, orboolean isSomething(..)
if the type of the property isboolean
.
-
The type of unbound field is determined from the return type of @FormField
-annotated method in the following way:
Java Type |
Lyra Form Type |
boolean |
BIT |
int |
INT |
ru.curs.celesta.dbutils.BLOB |
BLOB |
java.util.Date |
DATETIME |
String |
VARCHAR |
double |
REAL |
@FormField
-annotated method can either have or don’t have the CallContext
parameter.
The LyraFormField
object returned by the createField
method afterwards can be modified via its properties.
-
createAllBoundFields()
, which is equivalent to calling thecreateField
method for each of the table fields. -
createAllUnboundFields()
, which is equivalent to calling thecreateField
method for each of the properties of a class declared with@FormField
annotation.
For example, if we want all unbound fields in the form to go first, and then all bound fields, and we are satisfied with the default (CelestaDoc
or annotation-set) field property values, then we can write this:
public TestForm(CallContext context){
super(context);
createAllUnboundFields();
createAllBoundFields();
}
When writing a form constructor, the developer can choose one of the strategies so that the code is the most elegant, concise and flexible. As a rule, the choice of strategy is determined by one of the typical scenarios that one has to face:
Scenario |
Form construction strategy |
There is only one table-based form in the entire application. Or there can be many forms for one table, but on any form you need to display all the fields of the table or view in accordance with the CelestaDoc-specified properties. |
You should use the |
Only a very small quantity of the fields should be displayed on the form, or the form should be made very specific, not paying attention to what is indicated in CelestaDoc. |
You should use several calls to the |
In general, the properties specified in CelestaDoc are fine, but for some of the fields you need to override them. |
You must first use the |
Warning
|
Note that field names within a form must be unique, just as field names in a table.
Therefore, calling the createAllBoundFields() method twice, as well as calling the createField(name) method twice for the same name, will lead to an error.
An error will also result in creating an unbound field with a name coinciding with a table field added to the form.
|
afterReceiving(…) and beforeSending(…) Methods
A form class may and should also contain business logic that performs certain actions when values are entered by a user into a form. Two main entry points available in each of form classes are
void afterReceiving (BasicCursor c)
void beforeSending (BasicCursor c)
The afterReceiving (BasicCursor c)
method is called after receiving form data from the client, but before the data is flushed to the database.
Thus, if you change the cursor fields in it, then the changed values will be transferred into the database.
The argument c
contains a cursor with fields that come from the form.
The beforeSending (BasicCursor c)
method is called before serialization of data and sending it to the form.
Thus, if you change the fields in it, the modified values will be displayed on the form.
The argument c
contains a cursor with fields that come from the database.
Business logic can also be contained in getters and setters of unbound fields.
beforeShow(…) Method
The method is invoked before the form is displayed to the user. In this method, some preparatory actions can be performed: for example, the cursor can be positioned on the desired record.
Form Attributes
Each form has a set of attributes that can be defined using optional named parameters of @LyraForm
annotation:
-
gridwidth
— ширина грида (в пискелах) -
gridheight
— высота грида (в пикселах)
Field Attributes
Each form field (LyraFormField
class instance) has a set of following attributes:
-
caption
— 'human-readable' caption of the field. -
editable
— set tofalse
, is the field needs to be read-only. -
sortable
— indicates whether or not the grid should allow sorting by values in this field, by clicking on the column’s header cell. Defaults to true. -
visible
— set tofalse
, if the field needs to be hidden from form. -
required
— required field. Warning: bound fields related tonot null
table fields will be always treated as required, regardless of the value ofrequired
property. -
scale
— maximum decimal point numbers (forREAL
-typed fields). -
width
— visible width of the field (in pixels). -
dateFormat
— format of the datetime field.
Methods of Setting Field Attributes
So, the properties of form fields in Lyra can be set:
-
In design time:
-
for bound fields in table fields'
CelestaDoc
, -
for unbound fields by setting the properties of the
@FormField
annotation.
-
-
In run time: for any fields by changing the properties of the
LyraFormField
object, obtained either by calling thecreateField(name)
method, or by retrieving from the dictionary returned by thegetFieldsMeta()
method.
To set the field attributes for Lyra in CelestaDoc, you need to insert an object in JSON format into CelestaDoc, for example, like this:
CREATE TABLE table1
(
/** {"caption": "human-readable field name",
"visible": false}*/
column1 INT NOT NULL IDENTITY PRIMARY KEY,
/** игнорируемый текст {"caption": "field name with \"quoted\" words",
"editable": false,
"visible": true} this text will be ignored*/
column2 REAL,
column3 BIT NOT NULL DEFAULT 'FALSE'
);
Warning
|
Setting the field attributes in CelestaDoc is convenient because the attribute specified in one place (i. e. in the CelestaSQL script) will be used by default in all forms that use the corresponding table as a data source. If needed, in each specific form, you can always redefine attributes at run time. If the form using the table is only one, then the correct approach is to set the corresponding field attributes directly in CelestaDoc. Note that the system automatically selects from the CelestaDoc text the first occurring JSON object, ignoring the rest of the text content that may also be present there for other purposes. |
The @FormField
annotation is added to functions that return the values of unbound fields, and also has parameters caption
, editable
,` visible`, etc.
These are optional parameters that correspond to the field attributes of the same name.
If multiple values of the same property are defined in different places, they get overwritten in a certain order.
Property |
Precedence order for unbound fields |
Precedence order for bound fields |
|
1. @FormField annotation’s 2. if not set, then the getter method name. |
1. table field’s CelestaDoc ( |
|
1. @FormField annotation’s 2. or else |
1. CelestaDoc’s 2. if not set, then |
|
1. @FormField annotation’s 2. if not set, then |
1. table field’s CelestaDoc ( 2. if not set, then |
Lyra Forms Implementation
Below is a UML diagram of Lyra’s Java classes:
TODO: redraw this diagram in PlantUML
Implementation Example with Comments
@LyraForm(gridWidth = 100, gridHeight = 10)
public class TestForm extends BasicGridForm<OrderLineCursor> {
//Constructor will be run only once: each form is a Spring's singleton Component
public TestForm(CallContext c, GridRefinementHandler handler) {
super(c, handler);
//First, we add to the form all the table's fields in the order they declared in SQL
createAllBoundFields();
//Add a field to the form and then alter its caption
LyraFormField f = createField("field2");
f.setCaption("Unbound field caption");
//Add a field to a form with default attributes (inherited from CelestaDoc or chosen by default)
createField("field1");
}
@Override
public OrderLineCursor getCursor(CallContext callContext) {
//sorting and filtering can also be performed here
return new OrderLineCursor(callContext);
}
@FormField(caption = "Field Caption")
public String getField1(CallContext ctx) {
return "foo";
}
public void beforeSending(OrderLineCursor c){
//do something before the cursor is serialized and sent to the form
}
}
Client part
The Lyra config
The Lyra config should be specified as a function that returns the config object:
function getLyraConfig() { return { baseUrl: "http://localhost:8081", }; } window.getLyraConfig = getLyraConfig;
Property |
Description |
|
It’s a base URL to which the Lyra’s endpoint paths are appended. |
Lyra context
Lyra context is an object:
{ part1: 'part1', part2: 'part2', refreshParams: { selectKey: ['74000004000079300'], sort: ['name', 'code'], filter: { condition1: 'filter conditions1', condition2: 'filter conditions2', } } };
-
part1, part2 - arbitrary parameters,
-
refreshParams - parameters used to build the Lyra grid on the server:
-
selectKey - primary key for positioning on a specific record,
-
sort - sorting conditions,
-
filter - filter conditions.
-