--- name: fragments description: "Define reusable widget groups with CREATE FRAGMENT and place them with USE FRAGMENT. Use when the same widget pattern repeats across pages and should be written once." --- # Mendix Fragments Skill ## When to Use This Skill Use this skill when: - Defining reusable widget groups with `create fragment` - Inserting fragments into pages or snippets with `use fragment` - Listing or inspecting fragments with `list fragments` / `describe fragment` - Building multiple pages that share common widget patterns (footers, form fields, buttons) - Avoiding copy-paste of repeated widget structures across pages ## What Are Fragments? Fragments are **script-scoped, transient** widget groups: - Defined once, reused in multiple pages/snippets within the same script - **Not persisted** in the MPR file — they exist only during script execution - Widgets are deep-cloned on expansion (each USE gets independent copies) - Optional prefix support to avoid name conflicts when using the same fragment multiple times ## Syntax Reference ### CREATE FRAGMENT ```mdl mdl 1; create fragment SaveCancelFooter as { footer footer1 { actionbutton btnSave (caption: 'Save', action: save changes, buttonstyle: primary) actionbutton btnCancel (caption: 'Cancel', action: cancel changes) } }; ``` Multiple top-level widgets: ```mdl mdl 1; create fragment CustomerFields as { textbox txtName (label: 'Name', attribute: Name) textbox txtEmail (label: 'Email', attribute: Email) textbox txtPhone (label: 'Phone', attribute: Phone) }; ``` ### USE FRAGMENT Inside a page or snippet body: ```mdl mdl 1; create page Module.CustomerEdit ( params: ( $Customer: Module.Customer ), title: 'Edit Customer', layout: Atlas_Core.PopupLayout ) { dataview dvCustomer (datasource: $Customer) { use fragment CustomerFields use fragment SaveCancelFooter } }; ``` With prefix (avoids name conflicts): ```mdl use fragment SaveCancelFooter as order_ -- Creates: order_footer1, order_btnSave, order_btnCancel ``` ### Content Slots — wrap arbitrary content A plain fragment substitutes a **fixed** widget group. A **content slot** lets a fragment **wrap arbitrary caller-supplied content** — a reusable shell (a card, panel, or section) whose body varies per use. Declare a `slot` where the caller's widgets should land, then fill it with the `use fragment X { … }` payload form: ```mdl mdl 1; create fragment Card as { container cardWrap (class: 'card', designproperties: ('Card style': on)) { container cardBody (class: 'card-body') { slot content -- caller's widgets are spliced in here } } }; create page Module.Dashboard (title: 'Dashboard', layout: Atlas_Core.Atlas_Default) { use fragment Card { dynamictext cardHeading (content: 'Welcome', rendermode: H2) dynamictext cardText (content: 'Any widgets can go inside the reusable Card shell') } }; ``` Rules (v1): - The slot name is optional and defaults to `content`; a fragment supports one slot. - Using a slotted fragment with **no** payload (`use fragment Card`) expands the slot to nothing — a valid empty shell. - Supplying a payload to a fragment that declares **no** slot is an error. - The payload is deep-cloned; `as prefix_` still renames the fragment's own widgets (not the caller's payload). - The slot resolves at expansion — `describe page` shows the fully-expanded tree (no slot marker), and there are no BSON/round-trip surprises. > For varying a leaf **value** (a label or attribute name) rather than wrapping a > subtree, scalar params (`create fragment F($label, $attr) as …`) are a planned > v1.1 follow-up; today use a slot plus a one-line value fill. ### Parameter bindings — datasource & action (experimental) The content slot varies *structure* (which widgets). Typed **parameters** vary *data* and *behavior*: a fragment can declare a `datasource` and/or an `action` parameter, reference it with `$name` in a datasource/action position, and receive its value at the use site. This turns a shell into a real reusable component — one panel bound to a different entity and a different handler per use. ```mdl mdl 1; create fragment DataPanel($data: datasource, $onEdit: action) as { container panelWrap (class: 'card') { listview lvItems (datasource: $data) { slot content actionbutton btnEdit (caption: 'Edit', action: $onEdit, buttonstyle: primary) } } }; create page Module.Orders (title: 'Orders', layout: Atlas_Core.Atlas_Default) { use fragment DataPanel ($data: database Module.Order, $onEdit: microflow Module.EditOrder) { dynamictext panelHeading (content: 'Orders', rendermode: H4) } }; ``` Rules: - Param kinds are `datasource` (`$var` / `database E` / `$currentObject/Assoc` / `microflow M`) and `action` (a microflow / nanoflow / `save changes` / `show page` / …). - Every declared parameter must be supplied; unknown args and type mismatches are errors. - Values substitute at expansion — `describe page` shows the concrete datasource/action, no `$param`. **Building blocks** can't declare params (they're authored in Studio Pro), but a `use building block` accepts **rebind overrides** that rewrite the block's outermost datasource and/or its first button after the copy: ```mdl use building block Atlas_Web_Content.List_Cards (datasource: database Module.Order, action: call microflow Module.OpenOrder) as orders_; ``` Binding-point rule (prototype): datasource → the first widget carrying a datasource; action → the first button widget. For anything more specific, copy the block in with `as prefix_` and use `alter page … set … on prefix_widget`. ### LIST FRAGMENTS ```mdl list fragments; -- Lists all defined fragments with widget counts ``` ### DESCRIBE FRAGMENT ```mdl describe fragment SaveCancelFooter; -- Outputs the full MDL definition ``` ## Common Patterns ### Pattern 1: Standard CRUD Footer ```mdl create fragment CrudFooter as { footer footer1 { actionbutton btnSave (caption: 'Save', action: save changes, buttonstyle: primary) actionbutton btnCancel (caption: 'Cancel', action: cancel changes) } }; -- Use in every edit page create page Module.Customer_Edit (...) { dataview dv (datasource: $Customer) { textbox txtName (label: 'Name', attribute: Name) use fragment CrudFooter } }; create page Module.Order_Edit (...) { dataview dv (datasource: $Order) { textbox txtNumber (label: 'Order #', attribute: Number) use fragment CrudFooter } }; ``` ### Pattern 2: Form Field Groups ```mdl create fragment AddressFields as { textbox txtStreet (label: 'Street', attribute: Street) textbox txtCity (label: 'City', attribute: City) textbox txtZip (label: 'Zip Code', attribute: ZipCode) textbox txtCountry (label: 'Country', attribute: Country) }; -- Reuse in customer and supplier pages create page Module.Customer_Edit (...) { dataview dv (datasource: $Customer) { textbox txtName (label: 'Name', attribute: Name) use fragment AddressFields use fragment CrudFooter } }; ``` ### Pattern 3: Same Fragment with Prefix ```mdl create fragment ActionButtons as { actionbutton btnApprove (caption: 'Approve', action: save changes, buttonstyle: success) actionbutton btnReject (caption: 'Reject', action: cancel changes, buttonstyle: danger) }; create page Module.DualPanel (...) { layoutgrid lg { row { column (desktopwidth: 6) { use fragment ActionButtons as left_ } column (desktopwidth: 6) { use fragment ActionButtons as right_ } } } }; ``` ## Common Mistakes ### Duplicate Fragment Names ```mdl -- WRONG: Defining the same fragment name twice causes an error create fragment footer as { ... }; create fragment footer as { ... }; -- Error: fragment "Footer" already defined ``` ### Missing Fragment ```mdl -- WRONG: Using a fragment that hasn't been defined create page Module.MyPage (...) { use fragment NonExistent -- Error: fragment "NonExistent" not found }; ``` ### Name Conflicts Without Prefix ```mdl -- WRONG: Using same fragment twice without prefix creates duplicate widget names use fragment footer use fragment footer -- Widget name "footer1" already exists! -- CORRECT: Use prefix for uniqueness use fragment footer as first_ use fragment footer as second_ ``` ### Fragment Order ```mdl -- WRONG: Using a fragment before defining it create page Module.MyPage (...) { use fragment footer -- Error: fragment "Footer" not found }; create fragment footer as { ... }; -- CORRECT: Define before use create fragment footer as { ... }; create page Module.MyPage (...) { use fragment footer -- OK }; ``` ## Validation Checklist - [ ] All `create fragment` statements appear before their `use fragment` references - [ ] No duplicate fragment names in the script - [ ] Prefix used when the same fragment appears multiple times on one page - [ ] Fragment widget names don't conflict with other widgets on the page - [ ] All widgets inside fragments use valid syntax (same as page bodies) - [ ] A `use fragment X { … }` payload is only supplied when fragment `X` declares a `slot` - [ ] A slotted fragment has exactly one `slot` (v1 supports a single slot) ## Related Documentation - `mxcli syntax fragment` — CLI help topic - `create-page` — Page/widget syntax reference - `overview-pages` — CRUD page patterns - Proposal: `docs/11-proposals/proposal_page_composition.md`