initial
This commit is contained in:
@@ -0,0 +1,673 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user