master-detail-pages
The master-detail page pattern in MDL: a selectable list beside a detail form driven by SELECTION. Use when building a selection-based screen rather than a plain list or form.
SKILL.md.claude/skills/mendix/master-detail-pages/SKILL.md
GitHub<!-- skill-index: generated by scripts/skill-index.py; make check-skill-index -->
<!-- /skill-index -->
Find it fast
sed -n '<from>,<to>p' SKILL.md one section rather than reading the file;
./mxcli syntax <topic> answers how do I write this for a fraction of either.
| lines | section |
|---|---|
| 24-29 | Overview |
| 30-114 | MDL Syntax |
| 115-154 | Complete Example |
| 155-179 | Key Patterns |
| 180-198 | Syntax Summary |
| 199-204 | Related Skills |
| 205-210 | Implementation Notes |
Master-Detail Pages
Overview
Master-Detail is a common UI pattern showing:
- Master list (left): Selectable list of items (Gallery widget)
- Detail form (right): Form showing selected item details (DataView with SELECTION source)
MDL Syntax
Basic Structure
mdl 1;
create page Module.Entity_MasterDetail
(
title: 'Entity Master-Detail',
layout: Atlas_Core.Atlas_Default
)
{
layoutgrid mainGrid {
row {
-- Master list (4 columns)
column (desktopwidth: 4) {
gallery entityList (datasource: database Module.Entity, selection: single) {
template {
dynamictext name (content: '{1}', contentparams: ({1} = Name), rendermode: H4)
}
}
}
-- Detail form (8 columns)
column (desktopwidth: 8) {
dataview entityDetail (datasource: selection entityList) {
textbox txtName (label: 'Name', attribute: Name)
footer {
actionbutton btnSave (caption: 'Save', action: save changes, buttonstyle: success)
}
}
}
}
}
};
Key Components
1. GALLERY Widget (Master List)
gallery widgetName (
datasource: database from Module.Entity sort by Name asc,
selection: single|multiple|none
) {
template template1 {
-- Widgets for each item
dynamictext name (content: '{1}', contentparams: ({1} = AttrName), rendermode: H4)
}
}
Properties:
datasource: database from entity sort by attr asc|desc- Entity data source with optional sorting;sort by Assoc/Attr ascsorts over an associationselection: single- Selection mode (Single for master-detail)- Template content inside TEMPLATE widget (requires name)
2. DataView with SELECTION Source
dataview widgetName (datasource: selection sourceWidgetName) {
-- Form widgets
}
The selection source creates a binding to another widget's selection. When the user selects an item in the Gallery, the DataView displays that item.
3. LISTVIEW Widget (Nested Data)
listview widgetName (datasource: database Module.Entity, PageSize: 10) {
template template1 {
-- Widgets for each associated item
}
}
Used inside the detail form to show related/associated data.
Nested list by association (to-many): Use datasource: $currentObject/Module.Assoc (or the explicit datasource: association path form) on a list widget inside a parent DATAVIEW. Example: datagrid lines (datasource: $currentObject/Order_OrderLine) inside a dataview dv (datasource: database Order).
Referenced object by association (to-one) — "data from context": To show a single referenced object's attributes, use the same datasource: $currentObject/Module.Assoc on a nested DATAVIEW. Its children bind to the referenced (destination) entity. Example: an inner dataview dvEmployee (datasource: $currentObject/Expense_Employee) { textbox (attribute: Name) } inside a dataview dvExpense (datasource: $Expense) shows the Expense's Employee. (A DataView association source is stored as a Forms$DataViewSource with an association-navigating EntityRef — do not confuse it with the list-widget Forms$AssociationSource; that one is invalid on a DataView.)
Complete Example
mdl 1;
create page CRM.Customer_MasterDetail
(
title: 'Customer Management',
layout: Atlas_Core.Atlas_Default
)
{
layoutgrid mainGrid {
row {
column (desktopwidth: 4) {
dynamictext heading (content: 'Customers', rendermode: H3)
gallery customerList (datasource: database from CRM.Customer sort by Name asc, selection: single) {
template {
dynamictext name (content: '{1}', contentparams: ({1} = Name), rendermode: H4)
dynamictext email (content: '{1}', contentparams: ({1} = Email))
}
}
}
column (desktopwidth: 8) {
dataview customerDetail (datasource: selection customerList) {
dynamictext detailHeading (content: 'Customer Details', rendermode: H3)
textbox txtName (label: 'Name', attribute: Name)
textbox txtEmail (label: 'Email', attribute: Email)
textbox txtPhone (label: 'Phone', attribute: Phone)
footer {
actionbutton btnSave (caption: 'Save', action: save changes, buttonstyle: success)
actionbutton btnCancel (caption: 'Cancel', action: cancel changes)
}
}
}
}
}
};
Key Patterns
Selection Binding
The core of master-detail is the selection binding:
- Gallery has
selection: single- enables single item selection - DataView uses
datasource: selection galleryName- listens to Gallery selection - When user clicks an item in Gallery, DataView automatically updates
Widget Names
The selection binding uses widget names to connect:
- Gallery widget name:
customerList - DataView references:
datasource: selection customerList
Template Content with ContentParams
Inside Gallery templates, use contentparams to reference current item attributes:
template template1 {
dynamictext name (content: '{1}', contentparams: ({1} = Name), rendermode: H4)
dynamictext email (content: '{1}', contentparams: ({1} = Email))
}
Syntax Summary
| Element | Syntax |
|---|---|
| Page properties | (title: 'title', layout: Module.Layout) |
| Widget name | Required after type: gallery myGallery (...) |
| Database source | datasource: database from Module.Entity |
| Selection binding | datasource: selection widgetName |
| Sort by | datasource: database from entity sort by Name asc |
| Sort over an association | datasource: database from entity sort by Order_BillTo/City asc — one / per hop, last segment is the attribute. Name the hop when two associations reach the same entity; inference cannot tell them apart (mendixlabs/mxcli#1152) |
| Where filter | datasource: database from entity where [IsActive = true] |
| Selection mode | selection: single |
| Attribute binding | attribute: attributename |
| Action binding | action: save changes |
| Button style | buttonstyle: success |
| Text content | content: 'text' with contentparams: ({1} = attr) |
| Render mode | rendermode: H4 |
| Template content | template template1 { ... } |
Related Skills
- Overview Pages - CRUD page patterns
- Create Page - Basic page syntax
- ALTER PAGE/SNIPPET - Modify existing pages in-place (SET, INSERT, DROP, REPLACE)
Implementation Notes
- Gallery is a pluggable widget (similar to DataGrid2)
- Selection binding uses
ListenTargetSourcein the Model SDK - ListView is a built-in Mendix widget
- All widget properties use explicit
(key: value)syntax