Skip to content

Commit 0948144

Browse files
akoclaude
andcommitted
Fix COMBOBOX association: set EntityRef via IndirectEntityRef with association path
The COMBOBOX widget in association mode was missing the Entity property (CE0642) because the pluggable widget's attributeAssociation WidgetValue needs an EntityRef with the target entity resolved through the association. Uses DomainModels$IndirectEntityRef with a Steps array containing the association path and destination entity, which is how Mendix resolves entity references through associations in pluggable widgets. The integration test now validates the full COMBOBOX association flow passes mx check with zero errors (no more CE0642, CE8812, or CE1613). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 91ae1cf commit 0948144

3 files changed

Lines changed: 42 additions & 20 deletions

File tree

mdl/executor/cmd_pages_builder_input.go

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,39 @@ func setDataSource(val bson.D, ds pages.DataSource) bson.D {
172172
return result
173173
}
174174

175-
// setAssociationRef sets the AttributeRef field in a WidgetValue for an association binding.
176-
// Uses DomainModels$AttributeRef with Attribute field — the WidgetValue.AttributeRef property
177-
// in C# is statically typed as AttributeRef (not polymorphic MemberRef), so we cannot use
178-
// DomainModels$AssociationRef. The association is stored as a 3-part member path
179-
// Module.Entity.AssociationName (same format as attributes), and Mendix resolves it as a
180-
// member of the entity.
181-
func setAssociationRef(val bson.D, assocPath string) bson.D {
182-
return setAttributeRef(val, assocPath)
175+
// setAssociationRef sets the AttributeRef and EntityRef fields in a WidgetValue for an
176+
// association binding on a pluggable widget.
177+
// - assocPath: qualified association name (Module.AssociationName)
178+
// - entityName: target entity qualified name (Module.EntityName)
179+
// MxBuild requires both: AttributeRef with the association path (CE8812) and
180+
// EntityRef with the target entity (CE0642).
181+
// setAssociationRef sets the EntityRef field in a WidgetValue for an association binding
182+
// on a pluggable widget. Uses DomainModels$IndirectEntityRef which resolves through an
183+
// association path. MxBuild requires both the entity reference and association path (CE0642, CE8812).
184+
// The assocPath is the qualified association name (Module.AssociationName).
185+
// The entityName is the target entity qualified name (Module.EntityName).
186+
func setAssociationRef(val bson.D, assocPath string, entityName string) bson.D {
187+
result := make(bson.D, 0, len(val))
188+
for _, elem := range val {
189+
if elem.Key == "EntityRef" && entityName != "" {
190+
result = append(result, bson.E{Key: "EntityRef", Value: bson.D{
191+
{Key: "$ID", Value: mpr.IDToBsonBinary(mpr.GenerateID())},
192+
{Key: "$Type", Value: "DomainModels$IndirectEntityRef"},
193+
{Key: "Steps", Value: bson.A{
194+
int32(2), // version marker
195+
bson.D{
196+
{Key: "$ID", Value: mpr.IDToBsonBinary(mpr.GenerateID())},
197+
{Key: "$Type", Value: "DomainModels$IndirectEntityRefStep"},
198+
{Key: "Association", Value: assocPath},
199+
{Key: "DestinationEntity", Value: entityName},
200+
},
201+
}},
202+
}})
203+
} else {
204+
result = append(result, elem)
205+
}
206+
}
207+
return result
183208
}
184209

185210
// setAttributeRef sets the AttributeRef field in a WidgetValue.

mdl/executor/cmd_pages_builder_v3_pluggable.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ func (pb *pageBuilder) buildComboBoxV3(w *ast.WidgetV3) (*pages.CustomWidget, er
5959
return nil, fmt.Errorf("failed to build ComboBox datasource: %w", err)
6060
}
6161

62-
// 3. Set attributeAssociation — this is the association name (e.g., Order_Customer)
63-
// resolved to a 3-part member path: Module.Entity.AssociationName
64-
// (same format as attributes, since associations are entity members)
62+
// 3. Set attributeAssociation — association path + target entity
63+
// MxBuild requires both: association path in AttributeRef (CE8812) and
64+
// target entity in EntityRef (CE0642)
6565
if attr := w.GetAttribute(); attr != "" {
6666
assocPath := pb.resolveAssociationPath(attr)
6767
updatedObject = updateWidgetPropertyValue(updatedObject, propertyTypeIDs, "attributeAssociation", func(val bson.D) bson.D {
68-
return setAssociationRef(val, assocPath)
68+
return setAssociationRef(val, assocPath, entityName)
6969
})
7070
}
7171

mdl/executor/roundtrip_mxcheck_test.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -830,15 +830,12 @@ END;`
830830
if err != nil {
831831
if strings.Contains(output, "no longer exists") {
832832
t.Errorf("mx check reports attribute no longer exists (association not resolved correctly):\n%s", output)
833+
} else if strings.Contains(output, "CE0642") {
834+
t.Errorf("mx check reports Entity property missing on ComboBox (association EntityRef not set):\n%s", output)
835+
} else if strings.Contains(output, "CE8812") {
836+
t.Errorf("mx check reports association path missing on ComboBox:\n%s", output)
833837
} else if strings.Contains(output, "error") || strings.Contains(output, "Error") {
834-
// CE0642 "Property 'Entity' is required" on the DataView is a known
835-
// limitation of microflow-sourced dataviews — not related to the
836-
// ComboBox association fix under test.
837-
if strings.Contains(output, "CE0642") && !strings.Contains(output, "no longer exists") {
838-
t.Logf("mx check has unrelated DataView error (CE0642), ComboBox association resolved correctly:\n%s", output)
839-
} else {
840-
t.Errorf("mx check found errors:\n%s", output)
841-
}
838+
t.Errorf("mx check found errors:\n%s", output)
842839
} else {
843840
t.Logf("mx check output:\n%s", output)
844841
}

0 commit comments

Comments
 (0)