674 lines
20 KiB
Markdown
674 lines
20 KiB
Markdown
# Panels
|
|
|
|
Panels are UI screens defined entirely in XML. Vitruvio parses the XML against its XSD schema and renders Vaadin 7 components through a Java presenter layer — no Java UI code lives here.
|
|
|
|
---
|
|
|
|
## vitruvio.json Registration
|
|
|
|
```json
|
|
{
|
|
"key": "my-panel",
|
|
"name": "My Panel",
|
|
"description": "Short description.",
|
|
"category": "Category/Subcategory",
|
|
"displayOrder": 10,
|
|
"showInPresentation": false,
|
|
"openInNewWindow": false,
|
|
"showInMobileList": false,
|
|
"displayTimeInSeconds": 0,
|
|
"allowedGroups": ["group-key"],
|
|
"allowedUsers": [],
|
|
"forms": {
|
|
"desktop": "panels/my-panel/my-panel-desktop.xml",
|
|
"mobile": "panels/my-panel/my-panel-mobile.xml"
|
|
}
|
|
}
|
|
```
|
|
|
|
- `displayOrder` — use gaps (10, 20, 30…) to allow future insertions.
|
|
- `forms.mobile` is optional. Omit if there is no mobile form.
|
|
- `allowedGroups` and `allowedUsers` are additive — a user in any allowed group or listed directly gets access.
|
|
|
|
---
|
|
|
|
## Desktop Form — File Structure
|
|
|
|
```xml
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<panel-form xmlns="http://www.davinti.com.br/vitruvio/form/panel"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form/panel
|
|
https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-panel-form.xsd">
|
|
|
|
<form formKey="uniqueFormKey" width="100%" height="100%">
|
|
<name>Human-readable title</name>
|
|
<description>Short description.</description>
|
|
|
|
<initScript language="JavaScript">
|
|
<![CDATA[
|
|
function run() {
|
|
// called when the panel loads
|
|
}
|
|
]]>
|
|
</initScript>
|
|
|
|
<components>
|
|
<!-- root layout component -->
|
|
</components>
|
|
</form>
|
|
</panel-form>
|
|
```
|
|
|
|
- `formKey` must be unique within the panel. By convention it matches the panel key.
|
|
- `<initScript>` runs once when the panel is opened. The `run()` function is the entry point.
|
|
- Everything in `<components>` is the Vaadin component tree.
|
|
|
|
---
|
|
|
|
## Mobile Form — File Structure
|
|
|
|
Mobile forms use a different namespace and XSD, and support a `<ServerSide>` block for server-executed bridge functions.
|
|
|
|
```xml
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<panel-form xmlns="http://www.davinti.com.br/vitruvio/form/mobile/panel"
|
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:schemaLocation="http://www.davinti.com.br/vitruvio/form/mobile/panel
|
|
https://bitbucket.org/davinTI/vitruvio-xds/raw/master/vitruvio-mobile-panel-form.xsd">
|
|
|
|
<form formKey="uniqueFormKey">
|
|
<name>Title</name>
|
|
<description></description>
|
|
|
|
<initScript language="JavaScript">
|
|
<![CDATA[
|
|
function run() { ... }
|
|
]]>
|
|
</initScript>
|
|
|
|
<ServerSide>
|
|
<DataSources>
|
|
<SQLBuilderScriptDataSource id="dsName" connection-key="datasource-key"
|
|
language="JavaScript" workOnline="true">
|
|
<![CDATA[
|
|
function buildSQL(requestParams, sql_params) {
|
|
return "SELECT ...";
|
|
}
|
|
]]>
|
|
</SQLBuilderScriptDataSource>
|
|
</DataSources>
|
|
|
|
<Bridges>
|
|
<Bridge id="myBridge" language="JavaScript">
|
|
<![CDATA[
|
|
function execute(params) {
|
|
// runs on the server — full access to libService, db, etc.
|
|
return { result: ... };
|
|
}
|
|
]]>
|
|
</Bridge>
|
|
</Bridges>
|
|
</ServerSide>
|
|
|
|
<components>
|
|
<!-- mobile component tree -->
|
|
</components>
|
|
</form>
|
|
</panel-form>
|
|
```
|
|
|
|
### Calling bridges from mobile client scripts
|
|
```javascript
|
|
vCommunicationService.executeOnServer('myBridge', params).then(function(result) {
|
|
// handle result
|
|
}).catch(function(error) {
|
|
console.log('Error: ' + error);
|
|
});
|
|
```
|
|
|
|
### Mobile timers
|
|
```javascript
|
|
engine.startTimer('timerName', function() {
|
|
// runs every intervalMs
|
|
}, intervalMs);
|
|
|
|
engine.stopTimer('timerName');
|
|
```
|
|
|
|
---
|
|
|
|
## The `engine` API (Desktop)
|
|
|
|
`engine` is injected into all script contexts inside a panel.
|
|
|
|
### Fields
|
|
```javascript
|
|
var field = engine.getField('fieldId');
|
|
field.getValue(); // returns null when empty; for DB-backed fields returns a Java type — use == not === when comparing
|
|
field.setValue(value);
|
|
field.clear();
|
|
field.setEnabled(true/false);
|
|
field.setVisible(true/false);
|
|
field.setRequired(true/false);
|
|
field.focus();
|
|
field.refresh(); // DB components — re-runs the datasource query
|
|
```
|
|
|
|
### Layouts
|
|
```javascript
|
|
var layout = engine.getLayout('layoutId');
|
|
layout.getRootComposition().setVisible(true/false);
|
|
layout.getRootComposition().setCaption('New caption');
|
|
layout.getRootComposition().removeAllComponents();
|
|
layout.getRootComposition().addComponent(component);
|
|
layout.getRootComposition().setScrollTop(100000); // scroll to bottom
|
|
|
|
// WindowLayout only
|
|
layout.showWindow();
|
|
layout.closeWindow();
|
|
```
|
|
|
|
### User & Session
|
|
```javascript
|
|
var user = engine.getLoggedUser();
|
|
user.getLogin(); // username string
|
|
user.getNome(); // display name (desktop)
|
|
```
|
|
|
|
### Global variables — sharing state across components
|
|
```javascript
|
|
// Set in initScript or any handler
|
|
engine.setGlobalVariable('myHelper', function() { ... });
|
|
engine.setGlobalVariable('myData', { key: 'value' });
|
|
|
|
// Read anywhere in the form
|
|
engine.getGlobalVariable('myHelper')();
|
|
var data = engine.getGlobalVariable('myData');
|
|
|
|
// Clean up
|
|
engine.unsetGlobalVariable('myVar');
|
|
```
|
|
|
|
Use global variables to expose reusable functions (e.g. `limparCampos`, `carregarDados`) and shared state across components and events.
|
|
|
|
### Form validation
|
|
```javascript
|
|
if (engine.isValid()) {
|
|
// all required fields are filled
|
|
}
|
|
```
|
|
|
|
### Repeating timers (desktop polling)
|
|
```javascript
|
|
var timer = engine.registerRepeatingTimer(3000, function() {
|
|
// do something every 3s
|
|
// return true to keep running, false/undefined to stop
|
|
});
|
|
engine.setGlobalVariable('myTimer', timer);
|
|
|
|
// Later, to stop it:
|
|
engine.getGlobalVariable('myTimer').setEnabled(false);
|
|
engine.unsetGlobalVariable('myTimer');
|
|
```
|
|
|
|
---
|
|
|
|
## Script Blocks and Events
|
|
|
|
### initScript
|
|
Runs when the panel loads. Define helper functions here and expose them via `setGlobalVariable` so other components can call them.
|
|
|
|
```xml
|
|
<initScript language="JavaScript">
|
|
<![CDATA[
|
|
var db = libService.loadScript('db');
|
|
var banco = new db(db.VITRUVIO_DATASOURCE);
|
|
|
|
function carregarDados() { ... }
|
|
|
|
function run() {
|
|
engine.setGlobalVariable('carregarDados', carregarDados);
|
|
carregarDados();
|
|
}
|
|
]]>
|
|
</initScript>
|
|
```
|
|
|
|
### Button click
|
|
```xml
|
|
<ButtonWidget id="btnSalvar" caption="Salvar" defaultIcon="SAVE" style="GREEN">
|
|
<onClickScript language="JavaScript">
|
|
<![CDATA[
|
|
function run() {
|
|
// handle click
|
|
}
|
|
]]>
|
|
</onClickScript>
|
|
</ButtonWidget>
|
|
```
|
|
|
|
### Field value change
|
|
```xml
|
|
<TextField id="txfPesquisa" type="string" caption="Pesquisar" immediate="true">
|
|
<events>
|
|
<valueChange>
|
|
<script language="JavaScript">
|
|
<![CDATA[
|
|
function run() {
|
|
engine.getField('myTable').refresh();
|
|
}
|
|
]]>
|
|
</script>
|
|
</valueChange>
|
|
</events>
|
|
</TextField>
|
|
```
|
|
|
|
`immediate="true"` is required for `valueChange` to fire on every keystroke.
|
|
|
|
---
|
|
|
|
## Layout Components
|
|
|
|
### VerticalLayout / HorizontalLayout
|
|
```xml
|
|
<VerticalLayout width="100%" height="100%" spacing="true" margin="true"
|
|
id="myLayout" align="TOP_LEFT" expandRatio="1" visible="true"
|
|
backgroundColor="#f8b916">
|
|
```
|
|
|
|
**Sizing fields inside `layouts`:** always prefer `width="100%"` combined with `expandRatio` instead of fixed pixel widths. `expandRatio` controls the proportional share of the available space each child receives.
|
|
```xml
|
|
<HorizontalLayout width="100%" spacing="true">
|
|
<NumericField id="nmfId" width="100%" expandRatio="1" ... />
|
|
<TextField id="txfNome" width="100%" expandRatio="3" ... />
|
|
<ButtonWidget id="btnAcao" width="100%" expandRatio="1" ... />
|
|
</HorizontalLayout>
|
|
```
|
|
|
|
### Panel
|
|
A styled container with an optional caption and background color.
|
|
```xml
|
|
<Panel width="100%" height="100%" caption="Section title" backgroundColor="#f0f0f0" margin="true">
|
|
<VerticalLayout>...</VerticalLayout>
|
|
</Panel>
|
|
```
|
|
|
|
### CrudPanel
|
|
The standard page chrome: header bar + section content area.
|
|
|
|
> Validators will reject it as a root element. Always wrap it in a Layout element like `VerticalLayout`, `HorizontalLayout`, `TabLayout`, or `AbsoluteLayout` as the root component inside `<components>`.
|
|
|
|
```xml
|
|
<VerticalLayout width="100%" height="100%">
|
|
<CrudPanel width="100%" height="100%" expandRatio="1">
|
|
<Header>
|
|
<Caption>Page Title</Caption>
|
|
<SubCaption>Subtitle text</SubCaption>
|
|
</Header>
|
|
<Section caption="Tab label" subCaption="..." showHeader="false" width="100%" height="100%">
|
|
<VerticalLayout width="100%" height="100%">
|
|
<!-- content — Section requires a layout component (VerticalLayout, HorizontalLayout, etc.) as its direct child, not bare components -->
|
|
</VerticalLayout>
|
|
</Section>
|
|
</CrudPanel>
|
|
</VerticalLayout>
|
|
```
|
|
|
|
### TabLayout
|
|
```xml
|
|
<TabLayout width="100%">
|
|
<Tab caption="Tab 1">
|
|
<!-- content -->
|
|
</Tab>
|
|
<Tab caption="Tab 2">
|
|
<!-- content -->
|
|
</Tab>
|
|
</TabLayout>
|
|
```
|
|
|
|
### ScrollPanel
|
|
```xml
|
|
<ScrollPanel id="scrollArea" width="100%" height="100%">
|
|
<VerticalLayout id="content" width="100%">
|
|
<!-- scrollable content -->
|
|
</VerticalLayout>
|
|
</ScrollPanel>
|
|
```
|
|
|
|
### WindowLayout (modal)
|
|
```xml
|
|
<WindowLayout id="myModal" windowHeight="40%" windowWidth="40%"
|
|
windowResizable="false" windowClosable="false" windowModal="true">
|
|
<VerticalLayout width="100%" height="100%" margin="true">
|
|
<!-- modal content -->
|
|
</VerticalLayout>
|
|
</WindowLayout>
|
|
```
|
|
Open/close via `engine.getLayout('myModal').showWindow()` / `.closeWindow()`.
|
|
|
|
---
|
|
|
|
## Input Components
|
|
|
|
### Common attributes
|
|
| Attribute | Purpose |
|
|
| ------------- | ---------------------------------------------- |
|
|
| `id` | Required for `engine.getField()` access |
|
|
| `caption` | Label above the component |
|
|
| `width` | CSS width (`100%`, `200px`) |
|
|
| `expandRatio` | Flex grow ratio within parent layout |
|
|
| `visible` | `true`/`false` |
|
|
| `required` | Marks field as required for `engine.isValid()` |
|
|
| `enabled` | `true`/`false` |
|
|
| `description` | Tooltip text |
|
|
| `immediate` | `true` to fire events on every change |
|
|
| `maxLength` | Max character count (TextField, TextArea) |
|
|
|
|
### TextField / TextArea / NumericField / DateField
|
|
```xml
|
|
<TextField id="txfNome" type="string" caption="Nome" width="100%" required="true" maxLength="100" />
|
|
<TextArea id="txaDescricao" type="string" caption="Descrição" width="100%" />
|
|
<NumericField id="nmfCodigo" type="number" caption="Código" width="100%" enabled="false" />
|
|
<DateField id="dtInicio" type="date" caption="Data Início" resolution="DAY" width="100%" />
|
|
```
|
|
|
|
### Label (HTML content)
|
|
```xml
|
|
<Label id="lblInfo" contentMode="HTML" align="MIDDLE_CENTER">
|
|
<value>
|
|
<![CDATA[
|
|
<b style="font-size: 16px;">HTML content here</b>
|
|
]]>
|
|
</value>
|
|
</Label>
|
|
```
|
|
|
|
### ButtonWidget
|
|
```xml
|
|
<ButtonWidget id="btnAcao" caption="Label" defaultIcon="SAVE"
|
|
style="GREEN" width="100%" height="30px" expandRatio="1" align="MIDDLE_RIGHT"
|
|
keyCode="ENTER">
|
|
```
|
|
|
|
`style` values: `BLUE`, `RED`, `GREEN`, `GRAY`
|
|
`defaultIcon` values: `SAVE`, `ADD`, `EDIT`, `REMOVE`, `TRASH`, `SEARCH`, `BACK`, `SEND`, `ARROW_UP`, `ARROW_DOWN`
|
|
|
|
---
|
|
|
|
## DB Components
|
|
|
|
### DBTable
|
|
The primary data grid. Fetches data from a SQL datasource.
|
|
|
|
```xml
|
|
<DBTable id="dbtItems" type="number" immediate="true" width="100%"
|
|
height="100%" expandRatio="1" caption="Items"
|
|
showRowCount="true" drawRefreshButton="true" selectable="false"
|
|
rows="10" multivalue="false" exportXLS="true" useGridComponent="true">
|
|
|
|
<datasource>
|
|
<sqlBuilderDataSource connection-key="vitruvio" language="JavaScript">
|
|
<![CDATA[
|
|
function buildSQL(params) {
|
|
// params is a Map<String, Object> — put values here and reference with ${name} in SQL.
|
|
// Read form fields via engine (guard with if(engine) — datasource may run before form init).
|
|
var search = engine ? engine.getField('txfPesquisa').getValue() : null;
|
|
var searchValue = (search && String(search) != '') ? '%' + String(search) + '%' : '%';
|
|
params.put('search', searchValue);
|
|
return "SELECT * FROM MY_TABLE WHERE upper(NAME) LIKE upper(${search}) ORDER BY NAME";
|
|
}
|
|
]]>
|
|
</sqlBuilderDataSource>
|
|
</datasource>
|
|
|
|
<key-field>ID</key-field>
|
|
|
|
<columns>
|
|
<column name="ID" caption="Código" expand-ratio=".1" />
|
|
<column name="NAME" caption="Nome" expand-ratio=".6" />
|
|
<generated name="edit" caption="Editar" expand-ratio=".1">
|
|
<!-- see Generated Columns below -->
|
|
</generated>
|
|
</columns>
|
|
|
|
<bind>
|
|
<parameter value-type="string" defaultValue="" parameterName="parFilter" field-ref="txfFilter" />
|
|
</bind>
|
|
|
|
<events>
|
|
<valueChange>
|
|
<script language="JavaScript">
|
|
<![CDATA[
|
|
function run() {
|
|
var id = engine.getField('dbtItems').getValue();
|
|
// react to row selection
|
|
}
|
|
]]>
|
|
</script>
|
|
</valueChange>
|
|
</events>
|
|
|
|
<styleGenerator>
|
|
<scriptGenerator language="JavaScript">
|
|
<![CDATA[
|
|
function getStyle() {
|
|
return 'background-light-gray';
|
|
}
|
|
]]>
|
|
</scriptGenerator>
|
|
</styleGenerator>
|
|
|
|
</DBTable>
|
|
```
|
|
|
|
**Datasource variants:**
|
|
- `<sqlBuilderDataSource>` — dynamic SQL built in JavaScript; `engine` is available inside
|
|
- `<freeQuery connection-key="...">` — static SQL with `${paramName}` tokens bound via `<bind>`
|
|
- `<form-datasource ref="dsId" />` — reference a `<DataSources>` definition (mobile)
|
|
|
|
**Parameter syntax — important distinction:**
|
|
- `sqlBuilderDataSource` and `freeQuery`: use `${paramName}` in SQL, bound via `params.put()` or `<bind>`
|
|
- `db` library scripts (inline SQL): use `:paramName` syntax instead
|
|
|
|
**Note:** In `sqlBuilderDataSource`, always guard with `if (engine)` before calling `engine.getField(...)` because the datasource may be evaluated before the form is fully initialised.
|
|
|
|
### Generated Columns (action buttons in table rows)
|
|
```javascript
|
|
var vc = libService.loadScript('vaadinComponents');
|
|
|
|
function Generator() {
|
|
this.generate = function(itemId, columnId, item, container) {
|
|
var btn = vc.buttonIcon('Editar', function() {
|
|
var nome = item.getItemProperty('NAME').getValue();
|
|
engine.getField('txfNome').setValue(nome);
|
|
}, 'edit');
|
|
return btn;
|
|
}
|
|
}
|
|
var script = new Generator();
|
|
```
|
|
|
|
For delete with confirmation dialog:
|
|
```javascript
|
|
var vc = libService.loadScript('vaadinComponents');
|
|
importClass(Packages.br.com.davinti.base.vaadin.components.layout.ConfirmationBox);
|
|
|
|
function Generator() {
|
|
this.generate = function(itemId, columnId, item, container) {
|
|
var btn = vc.buttonIcon('Deletar', function() {
|
|
var listener = new ConfirmationBox.ConfirmationBoxListener() {
|
|
dialogEnd: function(context, action) {
|
|
if (action == ConfirmationBox.Action.YES) {
|
|
var db = libService.loadScript('db');
|
|
var banco = new db(db.VITRUVIO_DATASOURCE);
|
|
banco.update("DELETE FROM MY_TABLE WHERE ID = :id", { id: Number(itemId) });
|
|
engine.getField('dbtItems').refresh();
|
|
}
|
|
}
|
|
};
|
|
ConfirmationBox.show(
|
|
ConfirmationBox.DialogIcon.WARNING,
|
|
'Confirmar exclusão',
|
|
'Deseja excluir o item ' + item.getItemProperty('NAME').getValue() + '?',
|
|
listener,
|
|
ConfirmationBox.ACTION_YES_CANCEL
|
|
);
|
|
}, 'trash');
|
|
return btn;
|
|
}
|
|
}
|
|
var script = new Generator();
|
|
```
|
|
|
|
### DBTwinColSelect (multi-select from DB)
|
|
Multi-select widget populated from a query.
|
|
|
|
**Reading selected values:** `getValue()` on a `multivalue="true"` DBTwinColSelect returns a Java `Collection`, not a JS array. Use the Java iterator API:
|
|
```javascript
|
|
var selected = engine.getField('dbtwItems').getValue();
|
|
if (selected != null) {
|
|
var iter = selected.iterator();
|
|
while (iter.hasNext()) {
|
|
var item = String(iter.next());
|
|
// process item
|
|
}
|
|
}
|
|
```
|
|
|
|
**Setting values programmatically:** use a Java `ArrayList`:
|
|
```javascript
|
|
var itens = new java.util.ArrayList();
|
|
banco.query(sql, params).each(function(row) {
|
|
itens.add(java.lang.Long.valueOf(row.ID));
|
|
});
|
|
engine.getField('dbtwItems').setValue(itens);
|
|
```
|
|
|
|
### DBComboBox
|
|
Single-select dropdown from a query. Use `.getValue()` / `.setValue(val)` / `.clear()`.
|
|
|
|
---
|
|
|
|
## Feedback to the User
|
|
|
|
### MessageBox (desktop)
|
|
```javascript
|
|
importClass(Packages.br.com.davinti.base.vaadin.components.layout.MessageBox);
|
|
|
|
MessageBox.show(MessageBox.BoxType.SUCESS, 'Sucesso', 'Saved.');
|
|
MessageBox.show(MessageBox.BoxType.ERROR, 'Erro', 'Something failed.');
|
|
MessageBox.show(MessageBox.BoxType.WARNING, 'Atenção', 'Please check...');
|
|
```
|
|
|
|
Note: `BoxType.SUCESS` has no second S — that's the platform's spelling.
|
|
|
|
### Tray notification (non-blocking)
|
|
```javascript
|
|
var messages = libService.loadScript('messages');
|
|
var n = messages.notification;
|
|
n.show({
|
|
caption: 'Saved',
|
|
msg: 'Data saved successfully.',
|
|
delay: 3,
|
|
type: n.type.tray
|
|
});
|
|
```
|
|
|
|
### MessageBox (mobile)
|
|
The mobile API is different:
|
|
```javascript
|
|
MessageBox.show('Title', 'Message text');
|
|
MessageBox.showLoading('Loading...');
|
|
MessageBox.hideLoading();
|
|
MessageBox.confirm('Message', 'Title', [
|
|
{ text: 'Sim', handler: function() { ... } },
|
|
{ text: 'Não', handler: function() { ... } }
|
|
]);
|
|
```
|
|
|
|
---
|
|
|
|
## Loading Scripts and Libraries
|
|
|
|
```javascript
|
|
// In initScript (runs once, available to all handlers via globalVariable)
|
|
var libIA = libService.loadScript('IA_lib');
|
|
engine.setGlobalVariable('libIA', libIA);
|
|
|
|
// Inside a button or event handler
|
|
var db = libService.loadScript('db');
|
|
var banco = new db(db.VITRUVIO_DATASOURCE);
|
|
```
|
|
|
|
The `vaadinComponents` lib is used heavily in generated columns to create dynamic Vaadin components programmatically (buttons, layouts, labels, etc.).
|
|
|
|
---
|
|
|
|
## Database in Panels
|
|
|
|
Same as in endpoints — see the root CLAUDE.md for `db` usage notes. Key extras for panels:
|
|
|
|
```javascript
|
|
banco.getSequenceNextVal('MY_SEQUENCE'); // get next ID before insert
|
|
banco.transaction(function() {
|
|
this.update(sql1, params1);
|
|
this.update(sql2, params2);
|
|
});
|
|
banco.isOracle(); // true if Oracle — use for DB-dialect differences (SYSDATE vs CURRENT_TIMESTAMP, etc.)
|
|
```
|
|
|
|
---
|
|
|
|
## Field ID Naming Conventions
|
|
|
|
Prefix field IDs by component type for readability:
|
|
|
|
| Prefix | Component |
|
|
| ------------- | --------------- |
|
|
| `txf` | TextField |
|
|
| `txa` | TextArea |
|
|
| `ncf` / `nmf` | NumericField |
|
|
| `dbt` | DBTable |
|
|
| `dbb` | DBComboBox |
|
|
| `dbtw` | DBTwinColSelect |
|
|
| `btn` | ButtonWidget |
|
|
| `chk` | CheckBox |
|
|
| `lbl` | Label |
|
|
| `dt` | DateField |
|
|
| `cmb` | ComboBox |
|
|
|
|
---
|
|
|
|
## Common Patterns
|
|
|
|
### Show/hide navigation between views
|
|
Use multiple layouts within the same form and toggle visibility:
|
|
```javascript
|
|
engine.getLayout('layoutForm').getRootComposition().setVisible(true);
|
|
engine.getLayout('layoutList').getRootComposition().setVisible(false);
|
|
engine.getLayout('sectionHeader').getRootComposition().setCaption('New Caption');
|
|
```
|
|
|
|
### CRUD in a single form
|
|
The recurring pattern across panels:
|
|
1. **List view** (`layoutHome`) — DBTable + search field + "Add" button
|
|
2. **Form view** (`layoutAddItem`) — fields + Save + Back buttons, initially `visible="false"`
|
|
3. **initScript** exposes a `limparCampos` global that clears all form fields
|
|
4. **Edit** button in DBTable populates fields and switches to form view
|
|
5. **Save** checks `engine.isValid()`, upserts based on whether a key field has a value, then returns to list
|
|
6. **Back** clears fields and returns to list
|
|
|
|
### Reading system config
|
|
```javascript
|
|
var valor = vConfigService.getSystemConfigAsString('CONFIG_KEY');
|
|
```
|
|
|
|
### Getting the logged user in a script
|
|
```javascript
|
|
engine.getLoggedUser().getLogin() // desktop
|
|
engine.getLoggedUser().getNome() // desktop display name
|
|
engine.getLoggedUser().getName() // mobile display name
|
|
```
|