-
Couldn't load subscription status.
- Fork 6
Fix nested form collection types #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
For nested form collection types to work, we need to set different prototype name. In this example: field___name___sub___name__, both collection type have the same prototype name and will both be replaced when adding an item in the first collection type. To make field___name___sub___name2__ work, we need to read the prototype name from the dataset when replacing it in JavaScript.
Reviewer's guide (collapsed on small PRs)Reviewer's GuideThe form collection controller now reads a dynamic prototype placeholder name from a new data attribute and uses it to replace placeholders in nested form HTML via a regex, instead of relying on a hard-coded 'name'. Sequence diagram for dynamic prototype name replacement in addCollectionItemsequenceDiagram
participant JSController as FormCollectionController
participant Element as Form Element
JSController->>Element: Read dataset.prototype
JSController->>Element: Read dataset.prototypeName
JSController->>Element: Read dataset.index
JSController->>JSController: Replace all prototypeName in prototype with index
JSController->>Element: Update dataset.index
Class diagram for updated FormCollectionControllerclassDiagram
class FormCollectionController {
+element: HTMLElement
+addCollectionItem()
}
FormCollectionController : -prototype
FormCollectionController : -prototypeName
FormCollectionController : -index
FormCollectionController : +addCollectionItem()
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes an issue with nested form collection types where the default prototype name replacement mechanism fails. The problem occurs when multiple collection types share the same __name__ placeholder, causing incorrect replacements when adding items to nested collections.
- Introduces dynamic prototype name handling instead of hardcoded
__name__replacement - Reads the prototype name from the dataset to support unique placeholders for nested collections
- Updates the replacement logic to use a dynamic regex pattern
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there - I've reviewed your changes - here's some feedback:
- Provide a fallback to 'name' when dataset.prototypeName is missing to avoid creating a RegExp(/undefined/) and breaking replacements
- Escape any special regex characters in prototypeName before constructing the RegExp to prevent unintended matches or errors
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Provide a fallback to '__name__' when dataset.prototypeName is missing to avoid creating a RegExp(/undefined/) and breaking replacements
- Escape any special regex characters in prototypeName before constructing the RegExp to prevent unintended matches or errors
## Individual Comments
### Comment 1
<location> `assets-public/controllers/form_collection_controller.js:57` </location>
<code_context>
document.dispatchEvent(new Event('add.collection.item'))
let prototype = this.element.dataset.prototype
+ let prototypeName = this.element.dataset.prototypeName
// get the new index
const index = parseInt(this.element.dataset.index)
</code_context>
<issue_to_address>
Consider providing a fallback for prototypeName if the dataset property is missing.
Default prototypeName to '__name__' when it is undefined or empty to avoid unintended replacements and preserve previous behavior.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
let prototype = this.element.dataset.prototype
let prototypeName = this.element.dataset.prototypeName
// get the new index
const index = parseInt(this.element.dataset.index)
=======
let prototype = this.element.dataset.prototype
let prototypeName = this.element.dataset.prototypeName
if (!prototypeName) {
prototypeName = '__name__'
}
// get the new index
const index = parseInt(this.element.dataset.index)
>>>>>>> REPLACE
</suggested_fix>
### Comment 2
<location> `assets-public/controllers/form_collection_controller.js:62` </location>
<code_context>
+ // Replace prototype name (default '__name__') in the prototype's HTML to
// instead be a number based on how many items we have
- prototype = prototype.replace(/__name__/g, index)
+ const regex = new RegExp(prototypeName, 'g')
+ prototype = prototype.replace(regex, index)
// increase the index with one for the next item
</code_context>
<issue_to_address>
Escaping prototypeName before using it in RegExp can prevent unexpected behavior.
If prototypeName includes regex metacharacters, the replacement may fail. Escape prototypeName before using it in RegExp.
</issue_to_address>
<suggested_fix>
<<<<<<< SEARCH
// Replace prototype name (default '__name__') in the prototype's HTML to
// instead be a number based on how many items we have
const regex = new RegExp(prototypeName, 'g')
prototype = prototype.replace(regex, index)
=======
// Escape regex metacharacters in prototypeName
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Replace prototype name (default '__name__') in the prototype's HTML to
// instead be a number based on how many items we have
const regex = new RegExp(escapeRegExp(prototypeName), 'g')
prototype = prototype.replace(regex, index)
>>>>>>> REPLACE
</suggested_fix>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
For nested form collection types to work, we need to set different prototype name. In this example: field___name___sub___name__, both collection type have the same prototype name and will both be replaced when adding an item in the first collection type.
To make field___name___sub___name2__ work, we need to read the prototype name from the dataset when replacing it in JavaScript.
Summary by Sourcery
Use the prototypeName data attribute when replacing placeholders in the form collection controller to correctly handle nested collection types.
Bug Fixes:
Enhancements: