Master-Detail Pages

SkillDev tools

Lets your agent build master-detail screens with a selectable list beside a detail form in MDL.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Master-Detail Pages skill

About this capability

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.

What this skill tells your AI

The instructions your AI receives, as published by mendixlabs/mxcli in .claude/skills/mendix/master-detail-pages/SKILL.md and read by ahel’s review.

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

create page Module.Entity_MasterDetail
(
  title: 'Entity Master-Detail',
  layout: Atlas_Core.Atlas_Default
)
{
  layoutgrid mainGrid {
    row row1 {
      -- Master list (4 columns)
      column colMaster (desktopwidth: 4) {
        gallery entityList (datasource: database Module.Entity, selection: single) {
          template template1 {
            dynamictext name (content: '{1}', contentparams: [{1} = Name], rendermode: H4)
          }
        }
      }

      -- Detail form (8 columns)
      column colDetail (desktopwidth: 8) {
        dataview entityDetail (datasource: selection entityList) {
          textbox txtName (label: 'Name', attribute: Name)

          footer footer1 {
            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 asc sorts over an association
  • selection: 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

create page CRM.Customer_MasterDetail
(
  title: 'Customer Management',
  layout: Atlas_Core.Atlas_Default
)
{
  layoutgrid mainGrid {
    row row1 {
      column colMaster (desktopwidth: 4) {
        dynamictext heading (content: 'Customers', rendermode: H3)
        gallery customerList (datasource: database from CRM.Customer sort by Name asc, selection: single) {
          template template1 {
            dynamictext name (content: '{1}', contentparams: [{1} = Name], rendermode: H4)
            dynamictext email (content: '{1}', contentparams: [{1} = Email])
          }
        }
      }

      column colDetail (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 footer1 {
            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:

  1. Gallery has selection: single - enables single item selection
  2. DataView uses datasource: selection galleryName - listens to Gallery selection
  3. 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

ElementSyntax
Page properties(title: 'title', layout: Module.Layout)
Widget nameRequired after type: gallery myGallery (...)
Database sourcedatasource: database from Module.Entity
Selection bindingdatasource: selection widgetName
Sort bydatasource: database from entity sort by Name asc
Sort over an associationdatasource: 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 filterdatasource: database from entity where [IsActive = true]
Selection modeselection: single
Attribute bindingattribute: attributename
Action bindingaction: save_changes
Button stylebuttonstyle: success
Text contentcontent: 'text' with contentparams: [{1} = attr]
Render moderendermode: H4
Template contenttemplate template1 { ... }

Related Skills

Implementation Notes

  • Gallery is a pluggable widget (similar to DataGrid2)
  • Selection binding uses ListenTargetSource in the Model SDK
  • ListView is a built-in Mendix widget
  • All widget properties use explicit (key: value) syntax

Signals

GitHub stars
122
Forks
49
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
master-detail-pages
Source
github.com/mendixlabs/mxcli