Skip to content

Conversation

@jonasdekeukelaere
Copy link
Member

@jonasdekeukelaere jonasdekeukelaere commented Aug 20, 2025

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:

  • Prevent incorrect placeholder replacement in nested form collections by using the dynamic dataset prototypeName instead of the hardcoded 'name'

Enhancements:

  • Add support for custom prototypeName in the form collection controller to enable nested collections

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.
@sourcery-ai
Copy link

sourcery-ai bot commented Aug 20, 2025

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

The 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 addCollectionItem

sequenceDiagram
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
Loading

Class diagram for updated FormCollectionController

classDiagram
class FormCollectionController {
  +element: HTMLElement
  +addCollectionItem()
}
FormCollectionController : -prototype
FormCollectionController : -prototypeName
FormCollectionController : -index
FormCollectionController : +addCollectionItem()
Loading

File-Level Changes

Change Details Files
Introduce dynamic prototype placeholder support in form collection controller
  • Retrieve prototypeName from element.dataset
  • Update comment to describe dynamic placeholder replacement
  • Replace hard-coded 'name' replacement with regex built from prototypeName
assets-public/controllers/form_collection_controller.js

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

Copilot AI left a 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.

Copy link

@sourcery-ai sourcery-ai bot left a 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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@jonasdekeukelaere jonasdekeukelaere merged commit 8266192 into master Aug 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants