Last active
October 16, 2024 14:29
-
-
Save bjo3rnf/85e8490b98955cb6016f44445d9843fb to your computer and use it in GitHub Desktop.
Stimulus.js controller for Symfony collection form type with configurable item limit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% macro collection_item(form) %} | |
<div data-form-collection-target="field"> | |
{{ form_widget(form) }} | |
<button type="button" | |
data-action="form-collection#removeItem"> | |
remove | |
</button> | |
</div> | |
{% endmacro %} | |
{% import _self as formMacros %} | |
{{ form_start(form) }} | |
<div data-controller="form-collection" | |
data-form-collection-max-items-value="10" | |
data-form-collection-prototype-value="{{ formMacros.collection_item(form.collectionField.vars.prototype)|json_encode }}"> | |
<div data-form-collection-target="fields"> | |
{% do form.collectionField.setRendered %} | |
{% for field in form.collectionField %} | |
{{ formMacros.collection_item(field) }} | |
{% endfor %} | |
</div> | |
<button type="button" | |
data-action="form-collection#addItem" | |
data-form-collection-target="addButton"> | |
add | |
</button> | |
</div> | |
{{ form_rest(form) }} | |
{{ form_end(form) }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Controller } from 'stimulus' | |
export default class extends Controller { | |
static targets = [ 'fields', 'field', 'addButton' ] | |
static values = { | |
prototype: String, | |
maxItems: Number, | |
itemsCount: Number, | |
} | |
connect() { | |
this.index = this.itemsCountValue = this.fieldTargets.length | |
} | |
addItem() { | |
let prototype = JSON.parse(this.prototypeValue) | |
const newField = prototype.replace(/__name__/g, this.index) | |
this.fieldsTarget.insertAdjacentHTML('beforeend', newField) | |
this.index++ | |
this.itemsCountValue++ | |
} | |
removeItem(event) { | |
this.fieldTargets.forEach(element => { | |
if (element.contains(event.target)) { | |
element.remove() | |
this.itemsCountValue-- | |
} | |
}) | |
} | |
itemsCountValueChanged() { | |
if (false === this.hasAddButtonTarget || 0 === this.maxItemsValue) { | |
return | |
} | |
const maxItemsReached = this.itemsCountValue >= this.maxItemsValue | |
this.addButtonTarget.classList.toggle('hidden', maxItemsReached) | |
} | |
} |
Ok. The eventListener should be set with a data-action attribute. I did it this way because so I have not to set the target and the action. But u are right. It might be better to use the framework for that.
But the data-form-collection-target
on the button is missing anyway in your example html.
The itemsCountValueChanged()
function can not set a hidden class on a non existing target.
Yes, you are right. I added the missing target, thanks.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @MichaelBrauner,
the target is used to control if the add-button should be rendered or not depending on a possible upper limit of elements (see
itemsCountValueChanged()
. Moreover adding of event-handlers should be left to the framework to ensure they will be removed automatically as well.Cheers
Björn