Skip to content

Commit f2cb922

Browse files
claudeako
authored andcommitted
feat(alter-page): INSERT INTO container — append widgets as a container's children
ALTER PAGE could only INSERT BEFORE/AFTER a sibling widget, so there was no way to fill an EMPTY container or add a widget to a container/dataview without an existing sibling to anchor to. Add: alter page Mod.Page { insert into containerName { <widgets> } } which appends the widgets as the last children of the named container. Full-stack: grammar (INSERT INTO), visitor (Position "INTO"), executor (children take the target's own entity context, e.g. a dataview's entity), and both mutators (pagemutator for MPR files, MCP for live Studio Pro). An empty container omits its Widgets list, so it is created with the Mendix widget-list marker and written back into the parent (the bson.D grows). Simple containers are supported (DivContainer, Container, DataView, GroupBox, ScrollContainerRegion, Section); LayoutGrid and TabContainer have no single child list and give a clear error pointing to INSERT BEFORE/AFTER inside the target column/tab. Verified on 11.12.1: insert into an empty container, append to a non-empty one, and insert a data-bound widget into a dataview — mx check 0 errors. Unit test for the mutator INTO path + error case; doctype example; skill / quick-reference / syntax help updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
1 parent abba773 commit f2cb922

18 files changed

Lines changed: 240 additions & 7 deletions

File tree

.claude/skills/mendix/alter-page.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,22 @@ insert after txtName {
134134
insert before btnSave {
135135
actionbutton btnPreview (caption: 'Preview', action: microflow Module.ACT_Preview)
136136
}
137+
138+
-- Insert INTO a container — append as its last child (works on an EMPTY container)
139+
insert into ctnToolbar {
140+
actionbutton btnNew (caption: 'New', action: nothing, buttonstyle: primary)
141+
}
137142
```
138143
139144
Inserted widgets use the same syntax as `create page`. Multiple widgets can be inserted in a single block.
140145

146+
`insert into <container>` appends as the last child of the named container — the
147+
only way to fill an **empty** container, and handy for adding to a container/dataview
148+
without needing a sibling to anchor to. Widgets inserted into a dataview take that
149+
dataview's entity as their context. Supported on simple containers (container,
150+
dataview, groupbox, scroll-container region); for a layout grid or tab container,
151+
insert relative to a widget inside the target column/tab instead.
152+
141153
### DROP - Remove Widgets
142154

143155
```sql

cmd/mxcli/syntax/features_page.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func init() {
8989
"set property", "insert widget", "drop widget", "replace widget",
9090
"popup width", "popup height", "popup resizable",
9191
},
92-
Syntax: "ALTER PAGE Module.Name {\n SET property = value ON widgetName;\n SET (prop1 = val1, prop2 = val2) ON widgetName;\n SET Title = 'New Title'; -- page-level (case-sensitive)\n SET Class = 'css-class'; -- page-level CSS class / style\n SET Style = 'css: rule';\n SET PopupWidth = 800; -- page-level pop-up dimensions\n SET PopupHeight = 480;\n SET PopupResizable = true;\n INSERT AFTER widgetName { <widgets> };\n INSERT BEFORE widgetName { <widgets> };\n DROP WIDGET name1, name2;\n REPLACE widgetName WITH { <widgets> };\n};",
92+
Syntax: "ALTER PAGE Module.Name {\n SET property = value ON widgetName;\n SET (prop1 = val1, prop2 = val2) ON widgetName;\n SET Title = 'New Title'; -- page-level (case-sensitive)\n SET Class = 'css-class'; -- page-level CSS class / style\n SET Style = 'css: rule';\n SET PopupWidth = 800; -- page-level pop-up dimensions\n SET PopupHeight = 480;\n SET PopupResizable = true;\n INSERT AFTER widgetName { <widgets> };\n INSERT BEFORE widgetName { <widgets> };\n INSERT INTO containerName { <widgets> };\n DROP WIDGET name1, name2;\n REPLACE widgetName WITH { <widgets> };\n};",
9393
Example: "ALTER PAGE Module.EditPage {\n SET (Caption = 'Save & Close', ButtonStyle = Success) ON btnSave;\n INSERT AFTER txtName {\n TEXTBOX txtMiddleName (Label: 'Middle Name', Binds: MiddleName)\n };\n DROP WIDGET txtUnused;\n};",
9494
SeeAlso: []string{"page.create", "page.show", "snippet.alter"},
9595
})
@@ -152,7 +152,7 @@ func init() {
152152
Keywords: []string{
153153
"alter snippet", "modify snippet", "update snippet",
154154
},
155-
Syntax: "ALTER SNIPPET Module.Name {\n SET property = value ON widgetName;\n INSERT AFTER widgetName { <widgets> };\n INSERT BEFORE widgetName { <widgets> };\n DROP WIDGET name1, name2;\n REPLACE widgetName WITH { <widgets> };\n};",
155+
Syntax: "ALTER SNIPPET Module.Name {\n SET property = value ON widgetName;\n INSERT AFTER widgetName { <widgets> };\n INSERT BEFORE widgetName { <widgets> };\n INSERT INTO containerName { <widgets> };\n DROP WIDGET name1, name2;\n REPLACE widgetName WITH { <widgets> };\n};",
156156
Example: "ALTER SNIPPET Module.NavSnippet {\n REPLACE navItem1 WITH {\n ACTIONBUTTON btnHome (Caption: 'Home', Action: SHOW_PAGE Module.HomePage)\n };\n DROP WIDGET txtOldField;\n INSERT AFTER txtName {\n TEXTBOX txtNewField (Label: 'New Field', Binds: NewAttr)\n };\n};",
157157
SeeAlso: []string{"snippet", "page.alter"},
158158
})

docs-site/src/appendixes/quick-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ Modify an existing page or snippet's widget tree in-place without full `CREATE O
421421
| Page-level set | `SET Title = 'New Title'` | No ON clause for page properties |
422422
| Insert after | `INSERT AFTER widgetName { widgets }` | Add widgets after target |
423423
| Insert before | `INSERT BEFORE widgetName { widgets }` | Add widgets before target |
424+
| Insert into | `INSERT INTO containerName { widgets }` | Append as the container's last child (fills an empty container) |
424425
| Drop widgets | `DROP WIDGET name1, name2` | Remove widgets by name |
425426
| Replace widget | `REPLACE widgetName WITH { widgets }` | Replace widget subtree |
426427
| Pluggable prop | `SET 'showLabel' = false ON cbStatus` | Quoted name for pluggable widgets |

docs-site/src/examples/alter-page.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ ALTER PAGE CRM.Customer_Edit {
2020
};
2121
```
2222

23+
## Add a Widget Into a Container
24+
25+
Use `INSERT INTO` to append a widget as a container's last child — the only way to fill an empty container.
26+
27+
```sql
28+
ALTER PAGE CRM.Customer_Overview {
29+
INSERT INTO ctnToolbar {
30+
ACTIONBUTTON btnNew (Caption: 'New Customer', Action: NOTHING, ButtonStyle: Primary)
31+
}
32+
};
33+
```
34+
2335
## Remove Widgets
2436

2537
```sql

docs-site/src/language/alter-page.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,17 @@ ALTER PAGE Module.EditPage {
110110
ACTIONBUTTON btnPreview (Caption: 'Preview', Action: MICROFLOW Module.ACT_Preview)
111111
}
112112
};
113+
114+
-- Insert INTO a container — append as its last child (works on an empty container)
115+
ALTER PAGE Module.EditPage {
116+
INSERT INTO ctnToolbar {
117+
ACTIONBUTTON btnNew (Caption: 'New', Action: NOTHING, ButtonStyle: Primary)
118+
}
119+
};
113120
```
114121

122+
`INSERT INTO <container>` appends widgets as the container's last children — the only way to fill an **empty** container, and handy for adding to a container or data view without a sibling to anchor to. Widgets inserted into a data view take that data view's entity. Supported on simple containers; for a layout grid or tab container, insert relative to a widget inside the target column/tab instead.
123+
115124
The inserted widgets use the same syntax as in `CREATE PAGE`. Multiple widgets can be inserted in a single block.
116125

117126
### DROP WIDGET -- Remove Widgets

docs-site/src/language/snippets.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ ALTER SNIPPET MyModule.CustomerCard {
9797
SET Caption = 'View Details' ON btnEdit;
9898
INSERT AFTER txtEmail {
9999
DYNAMICTEXT txtPhone (Content: '{1}', Attribute: Phone)
100+
};
101+
INSERT INTO ctnActions { -- append as the container's last child
102+
ACTIONBUTTON btnMore (Caption: 'More', Action: NOTHING)
100103
}
101104
};
102105
```

docs-site/src/reference/page/alter-page.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ SET 'propertyName' = value ON widgetName;
2929
INSERT BEFORE widgetName { widget_definitions };
3030
INSERT AFTER widgetName { widget_definitions };
3131

32+
-- Insert widgets as the last children of a container
33+
INSERT INTO containerName { widget_definitions };
34+
3235
-- Remove widgets
3336
DROP WIDGET widgetName1, widgetName2;
3437

@@ -68,6 +71,12 @@ For pluggable widget properties, use quoted property names (e.g., `'showLabel'`)
6871

6972
Inserts new widgets immediately before or after a named widget within its parent container. The new widgets use the same syntax as in `CREATE PAGE`.
7073

74+
### INSERT INTO
75+
76+
Appends new widgets as the **last children** of a named container. This is the only way to fill an **empty** container, and the natural way to add a widget to a container or data view without needing a sibling to anchor to. Widgets inserted into a data view take that data view's entity as their context.
77+
78+
Supported on simple containers (container/DivContainer, data view, group box, scroll-container region). A layout grid (rows/columns) and tab container have no single child list — insert relative to a widget inside the target column or tab instead.
79+
7180
### DROP WIDGET
7281

7382
Removes one or more widgets by name. The widget and all its children are removed from the tree.

docs/01-project/MDL_QUICK_REFERENCE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,7 @@ Modify an existing page or snippet's widget tree in-place without full `create o
982982
| Widget dynamic classes | `set DynamicClasses = 'expr' on widgetName` | Runtime-computed classes on a widget — the surgical alternative to a bulk `update widgets` |
983983
| Insert after | `insert after widgetName { widgets }` | Add widgets after target |
984984
| Insert before | `insert before widgetName { widgets }` | Add widgets before target |
985+
| Insert into | `insert into containerName { widgets }` | Append as the container's last child (fills an empty container; dataview children take its entity) |
985986
| Drop widgets | `drop widget name1, name2` | Remove widgets by name |
986987
| Replace widget | `replace widgetName with { widgets }` | Replace widget subtree |
987988
| Pluggable prop | `set 'showLabel' = false on cbStatus` | Quoted name for pluggable widgets |

docs/05-mdl-specification/01-language-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ set title = 'New Title';
838838
-- Insert widgets
839839
insert after widgetName { <widgets> };
840840
insert before widgetName { <widgets> };
841+
insert into containerName { <widgets> }; -- append as the container's last child
841842

842843
-- Remove widgets
843844
drop widget name1, name2;

mdl-examples/doctype-tests/33-alter-page-examples.mdl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,19 @@ alter page AlterPg.Product_Overview {
131131
}
132132
};
133133

134+
-- MARK: AP02b — INSERT INTO a container (append as last child)
135+
136+
-- ============================================================================
137+
-- AP02b: INSERT INTO — append widgets as a container's children. This is the
138+
-- only way to fill an EMPTY container (here, the `divider` just created above).
139+
-- ============================================================================
140+
141+
alter page AlterPg.Product_Overview {
142+
insert into divider {
143+
dynamictext dividerNote (Content: 'Catalog below', RenderMode: paragraph)
144+
}
145+
};
146+
134147
-- MARK: AP03 — INSERT a DataGrid2 with initial columns
135148

136149
-- ============================================================================

0 commit comments

Comments
 (0)