Skip to content

Commit 2eff994

Browse files
authored
fix(vue): improve Flexible field context detection with multi-method approach (#6)
Key improvements: - Add detectFlexibleContextOnMount() for early context detection - Detect context from container attribute, child fields, parent components, and DOM - Only cache base attribute values when context matches event prefix - Filter events strictly once context is detected - Claim context only from relevant events (matching dependency fields) This fixes cross-group contamination in multi-group Flexible layouts where the first event would incorrectly set context for all containers. Fixes #4
1 parent ef35992 commit 2eff994

2 files changed

Lines changed: 218 additions & 120 deletions

File tree

resources/js/components/DetailField.vue

Lines changed: 79 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,85 @@ export default {
4545
},
4646
4747
mounted() {
48+
this.detectFlexibleContextOnMount();
4849
this.checkDependencies();
4950
},
5051
5152
methods: {
53+
/**
54+
* Attempt to detect Flexible field context at mount time.
55+
*/
56+
detectFlexibleContextOnMount() {
57+
// Method 1: Check container's own attribute
58+
const ownAttribute = this.field?.attribute || '';
59+
if (ownAttribute) {
60+
const prefix = this.extractPrefixFromAttribute(ownAttribute);
61+
if (prefix) {
62+
this.cachedContextPrefix = prefix;
63+
return;
64+
}
65+
}
66+
67+
// Method 2: Check child field attributes
68+
const childFields = this.field?.fields || [];
69+
for (const child of childFields) {
70+
if (child.attribute) {
71+
const childPrefix = this.extractPrefixFromAttribute(child.attribute);
72+
if (childPrefix) {
73+
this.cachedContextPrefix = childPrefix;
74+
return;
75+
}
76+
}
77+
}
78+
79+
// Method 3: Walk up the Vue component tree to find Flexible layout
80+
let parent = this.$parent;
81+
let depth = 0;
82+
const maxDepth = 20;
83+
84+
while (parent && depth < maxDepth) {
85+
// Check for Flexible layout indicators
86+
if (parent.layout !== undefined || parent.layoutIndex !== undefined || parent.groupIndex !== undefined) {
87+
const flexKey = parent.field?.attribute || parent.attribute || '';
88+
const index = parent.layoutIndex ?? parent.groupIndex ?? parent.index ?? 0;
89+
90+
if (flexKey) {
91+
this.cachedContextPrefix = `${flexKey}__${index}__`;
92+
return;
93+
}
94+
}
95+
96+
// Check parent's field for Flexible prefix pattern
97+
if (parent.field?.attribute) {
98+
const prefix = this.extractPrefixFromAttribute(parent.field.attribute);
99+
if (prefix) {
100+
this.cachedContextPrefix = prefix;
101+
return;
102+
}
103+
}
104+
105+
parent = parent.$parent;
106+
depth++;
107+
}
108+
109+
// Method 4: Check all fields to find our context
110+
this.$nextTick(() => {
111+
if (!this.cachedContextPrefix) {
112+
const allFields = this.getAllFields();
113+
for (const f of allFields) {
114+
if (f.attribute) {
115+
const prefix = this.extractPrefixFromAttribute(f.attribute);
116+
if (prefix) {
117+
this.cachedContextPrefix = prefix;
118+
this.checkDependencies(); // Re-check with detected context
119+
return;
120+
}
121+
}
122+
}
123+
}
124+
});
125+
},
126+
52127
checkDependencies() {
53128
if (!this.field.dependencies || this.field.dependencies.length === 0) {
54129
this.isVisible = true;
@@ -122,17 +197,16 @@ export default {
122197
}
123198
124199
// For Flexible fields: resolve attribute relative to current context
125-
const contextPrefix = this.getFlexibleContextPrefix();
126-
if (contextPrefix) {
200+
if (this.cachedContextPrefix) {
127201
// Try to find field with same prefix (within same Flexible group)
128-
const prefixedAttribute = `${contextPrefix}${attribute}`;
202+
const prefixedAttribute = `${this.cachedContextPrefix}${attribute}`;
129203
const prefixedMatch = allFields.find(f => f.attribute === prefixedAttribute);
130204
if (prefixedMatch) {
131205
return prefixedMatch;
132206
}
133207
134208
// Also try alternative Flexible attribute formats
135-
const alternativeFormats = this.getFlexibleAttributeFormats(contextPrefix, attribute);
209+
const alternativeFormats = this.getFlexibleAttributeFormats(this.cachedContextPrefix, attribute);
136210
for (const format of alternativeFormats) {
137211
const match = allFields.find(f => f.attribute === format);
138212
if (match) {
@@ -199,48 +273,6 @@ export default {
199273
return null;
200274
},
201275
202-
/**
203-
* Detect the Flexible field context prefix from the container's own field attribute.
204-
* Flexible fields use prefixes like: flexible_key__index__ or flexible_key[index]
205-
*/
206-
getFlexibleContextPrefix() {
207-
// Return cached prefix if available
208-
if (this.cachedContextPrefix) {
209-
return this.cachedContextPrefix;
210-
}
211-
212-
// Check if this container has a prefixed attribute (indicating it's inside a Flexible field)
213-
const ownAttribute = this.field?.attribute || '';
214-
215-
// Pattern 1: Double underscore format (e.g., "overlay_items__0__field_name")
216-
const underscoreMatch = ownAttribute.match(/^(.+__\d+__)/);
217-
if (underscoreMatch) {
218-
this.cachedContextPrefix = underscoreMatch[1];
219-
return this.cachedContextPrefix;
220-
}
221-
222-
// Pattern 2: Bracket format (e.g., "overlay_items[0][field_name]")
223-
const bracketMatch = ownAttribute.match(/^(.+\[\d+\]\[)/);
224-
if (bracketMatch) {
225-
this.cachedContextPrefix = bracketMatch[1];
226-
return this.cachedContextPrefix;
227-
}
228-
229-
// Try to detect from child field attributes (inside the container)
230-
const childFields = this.field?.fields || [];
231-
for (const child of childFields) {
232-
if (child.attribute) {
233-
const childPrefix = this.extractPrefixFromAttribute(child.attribute);
234-
if (childPrefix) {
235-
this.cachedContextPrefix = childPrefix;
236-
return this.cachedContextPrefix;
237-
}
238-
}
239-
}
240-
241-
return null;
242-
},
243-
244276
/**
245277
* Generate alternative attribute formats for Flexible fields.
246278
*/
@@ -284,4 +316,4 @@ export default {
284316
.nova-dependency-container {
285317
transition: all 0.3s ease;
286318
}
287-
</style>
319+
</style>

0 commit comments

Comments
 (0)