The Form (Input Panel) protocol allows all field setup, check, and update operations to be handled through a single stateless Kokos update service. The form therefore provides a continuous passing of values between the client and the server by exploiting the dynamicity of the view.
The form protocol handle:
Data interface to exchange between client and server is as follow:
SmeupDataDataset
/**
* Represent the interface of a Form, List, Table, etc. (a unique dataset for all structures)
*/
interface SmeupDataDataset extends SmeupDataStructure {
columns: SmeupDataColumn[];
rows: SmeupDataRow[];
}
/**
* Represent the description and order of the fields.
* It groups commos properties related to fields
*/
interface SmeupDataColumn {
name: string;
title?: string; // field label
// visible refers to visibility of the field (it is correct on column layer)
visible?: boolean; // io=h
}
/**
* Represent the values of the fields.
*/
interface SmeupDataRow {
cells?: {
[columnName: string]: SmeupDataCell;
};
layout?: SmeupDataLayout;
}
/**
* Represent the value of a field.
*/
interface SmeupDataCell {
value?: string;
obj?: {
t: string;
p: string;
k: string;
};
// multiple options (i.e. radio, checkbox, select, etc.)
options?: SmeupDataCellOption[];
icon?: string;
editable?: boolean; // io=b
mandatory?: boolean; // cio
shape?: SmeupCellShapes;
fun?: string; // function to execute to retrieve the options
}
/**
* Reprensent the single option of the cell
*/
interface SmeupDataCellOption {
id: string;
label: string;
}
/**
* Represent the layout of the fields in a form and the adjuntive informations
*/
interface SmeupDataLayout {
horizontal?: boolean;
sections?: SmeupDataLayoutSection[];
}
/**
* Represent a section of the layout of the fields in a form
*/
interface SmeupDataLayoutSection {
id?: string;
content?: SmeupDataLayoutField[];
sections?: SmeupDataLayoutSection[];
// layout classic attributes
dim?: string;
horizontal?: boolean;
// grid attributes (each section is a grid)
gridCols?: number;
gridRows?: number;
gap?: number;
}
/**
* Represent the presentation information of a field in a form
*/
interface SmeupDataLayoutField {
id: string;
// grid attributes
colSpan?: number;
colStart?: number;
colEnd?: number;
rowSpan?: number;
rowStart?: number;
rowEnd?: number;
}
/**
* Represent the possible shapes of a field
*/
enum SmeupCellShapes {
AUTOCOMPLETE = 'ACP',
BUTTON_LIST = 'BTN',
BUTTON = 'BTN',
CHART = 'GRA',
CHECKBOX = 'CHK',
CHIP = 'CHI',
COLOR_PICKER = 'CLP',
COMBOBOX = 'CMB',
EDITOR = 'EDT',
GAUGE = 'GAU',
IMAGE = 'IMG',
INPUT_CHECKBOX = 'INC',
INPUT_FIELD = 'INF',
KNOB = 'KNB',
MULTI_AUTOCOMPLETE = 'AML',
MULTI_COMBOBOX = 'CML',
PROGRESS_BAR = 'PGB',
RADIO = 'RAD',
RATING = 'RTG',
SWITCH = 'SWT',
TEXT_FIELD = 'ITX',
}
Form protocol is based on 2 step:
F(FOR;[SERVICE_NAME];*SETUP)
The setup phase is the first executed and lets the client know how the form is to be designed. The server then returns the form setup interface, containing the definition of the fields, their default values and optionally the layout.
The client then reads the SmeupDataDataset
interface and through the value of columns
and rows
, renders the fields in the webup page. Each SmeupDataColumn
corresponds to an object
in the form. The data of the latter is specified in the SmeupDataCell
of the SmeupDataRow
. The form objects are translated into graphical shapes (ketchup components) starting from their semantic value (SmeupCellShapes
). The default is always SmeupCellShapes.TEXT_FIELD
.
The graphic shape can be forced
at the column level but is usually attributed by default to the object specified in the cell.
The layout of the fields is also declared in the SmeupDataDataset
interface specifically at the level of SmeupDataRow
. Regarding the layout we want to try to introduce a new feature: Grid System.
The classic version of the Smeup layout is based on the division of sections into vertical (letters) and horizontal (numbers). For the layout of the input panel we want to try to define a single section that indicates the entire grid and flank each field with its position in the grid.
The update phase is concerned with sending the value of fields from client to server and possible re-render of the form. The fields are sent exploiting the standard Smeup format (key(value)
). The fun therefore has the following format:
F(FOR;[SERVICE_NAME];*UPDATE) INPUT(BEFORE(FLD_1(VAL_1) FLD_2(VAL_2)) AFTER(FLD_1(VAL_1) FLD_2(VAL_2)))
As per the template we note that the payload contains two specific keywords:
The advantage of such an implementation is that the communication remains 100% stateless and the server-side developer has the ability to view the previously returned value without having to rely on a database. Of course, there is nothing to prevent ignoring the BEFORE
and exploiting only the last updated value. However, it is important to keep in mind that the server-side service must be able to handle transactions properly.
Upon execution of the update, the server may return either completion feedback, a new field render (e.g., the configurator), or error feedback.
Regarding completion feedback, it is possible to insert informational messages as elements of SmeupDataDataset.messages
.
{
"messages": [
{
"gravity": "INFO",
"message": "Form completed correctly"
}
]
}
New field rendering concerns optional next steps. Additional field related to the value of previous fields. For example of application cases may be those related to the configurator, or password reset forms with the OTP... . In this case being stateless communication it is only necessary to return a new SmeupDataDataset
interface containing the new fields. The protocol remains as it is.
Finally, error handling is done by means of messages. To the single element of messages
, as seen in previous, it is also possible to indicate the id of the referenced field. For example:
{
columns: [
{
name: 'role',
title: 'Role',
},
],
rows: [
{
cells: {
role: {
obj: {
t: 'TA',
p: 'ROL',
k: '',
},
value: 'SOLUTION ARCHITECT',
},
},
},
],
messages: [
{
gravity: 'ERROR',
message: 'SOLUTION ARCHITECT is not a valid role',
cellId: 'role',
},
],
};
The message specifies the id of the referenced cell.
In a form it is also possible to have additional buttons that have completely different meanings. These buttons in the interface are ordinary fields with the difference that they specify the graphical form SmeupCellShapes.BUTTON
and a Fun. The FUN.
{
columns: [
{
name: 'employee',
title: 'Employee',
},
{
name: 'role',
title: 'Role',
},
{
name: 'clear',
},
{
name: 'submit',
},
],
rows: [
{
cells: {
employee: {
obj: {
t: 'CN',
p: 'COL',
k: '',
},
value: '',
options: ['BONMAI', 'SANBRU', 'FOSLUC'],
},
role: {
obj: {
t: 'TA',
p: 'ROL',
k: '',
},
value: '',
options: ['DEVELOPER', 'ANALYST', 'MANAGER'],
},
clear: {
obj: {
t: 'J4',
p: 'BTN',
k: '',
},
icon: 'clear',
value: 'Help',
shape: SmeupCellShapes.BUTTON,
fun: 'F(EXB;[SERVICE_NAME];*CLEAR)',
},
submit: {
obj: {
t: 'J4',
p: 'BTN',
k: '',
},
value: 'Submit',
shape: SmeupCellShapes.BUTTON,
fun: 'F(EXB;[SERVICE_NAME];*UPDATE)',
},
},
},
],
}
For example, in the previous interface you can see an additional button (clear) that on click invokes the F(EXB;[SERVICE_NAME];*CLEAR)
fun. The implementation of the fun can be done either at the service level itself or even in different services. It will be up to the developer to make the choice. However, it is advisable to keep the form-related logic as neat as possible.