Apps, forms, and controls#
Business Language includes UI declarations for apps, forms, controls, and source-driven layout. These declarations describe the user interface without embedding configurable business policy in UI code.
Apps#
Apps can be written with a header block followed by the member body, or with inline header properties inside the body.
app BackOffice {
title: "Back Office";
form vendors: VendorForm {
layout: "wide";
}
toolbar: {
save: "Save" -> save_vendor icon: "save";
}
command_palettes: {
vendors: "Open vendors" -> open_vendors;
}
navigation: {
vendors: {
form: vendors;
label: "Vendors";
}
}
ui: {
layout: "desktop";
}
startup: vendors;
}Apps can declare mounted forms, state, toolbar sections, command palettes, navigation, UI config, and startup form references.
app BackOffice {
title: "Back Office";
} {
state {
selected_vendor_id: VendorId;
}
form vendors: VendorForm {
layout: two_column;
}
toolbar: main:
"Save" -> save_vendor icon: "save";
command_palettes: global:
"Open vendors" -> open_vendors shortcut: "Ctrl+K";
navigation: {
vendors {
form: vendors;
label: "Vendors";
}
}
ui: {
layout: desktop;
}
startup: vendors;
}Mounted form layout presets include vertical, horizontal, two_column, and grid. App state declarations are typed members and may also appear directly in the app body.
Forms#
Forms also support optional header properties before the member body.
form VendorForm readonly : BaseForm {
title: "Vendor";
state {
selected_vendor_id: VendorId;
}
body: {
required vendor_name: Text from vendor.vendor_name {
label: "Vendor name";
on change {
validate_vendor();
}
}
section address {
city: Text {
label: "City";
}
}
}
toolbar {
save: PrimaryButton() {
text: "Save";
on click {
save_vendor();
}
}
}
layout: {
columns: 2;
};
}Form fields support required, optional, readonly, disabled, and hidden. Field bodies can contain properties, nested property blocks, and event handlers.
form VendorForm {
title: "Vendor";
} {
selected_vendor_id: VendorId;
body: {
@label("Vendor")
readonly vendor_name: Text from vendor.vendor_name {
label: "Vendor name";
display format {
uppercase: true;
}
}
}
}Form fields can specify a control type and source with from, a control type without from, or a source without an explicit control type.
form SourceBackedForm {
body: {
customer_grid: DataGrid from CustomerMaster;
customer_group: from CustomerMaster;
customer_name: TextInput from CustomerMaster.customer_name;
}
}Buttons and menus#
Forms can declare reusable button templates and button instances. Button members include properties, event handlers, and nested menus.
form VendorForm {
button ActionButton<T>(label: string) {
text: label;
on click {
run_action();
}
}
toolbar {
save: PrimaryButton() {
text: "Save";
menu {
option save_and_close {
text: "Save and close";
on click {
save_and_close();
}
}
separator;
}
}
}
}Controls#
Controls may use a header block for presentation metadata.
control AddressBlock {
label: "Address";
on change {
validate_address();
}
children: {
control StreetInput {
placeholder: "Street";
}
}
}Controls contain properties, event handlers, and child controls.
control AddressBlock {
title: "Address";
} {
label: "Address";
on change {
validate_address();
}
children: {
control StreetInput {
placeholder: "Street";
}
}
}Source UI layout#
Source UI layout blocks are useful for table or source-driven UI composition. In BL source, use them as the value of a ui: member on a table or class.
For the full syntax, see Source UI layouts.
{
group "Main" {
title: "General";
row {
field vendor_name;
field email;
}
insert row after field vendor_name {
field phone;
}
hide field email with field phone;
}
}Layout groups can contain properties, rows, row operations, and field operations.