Skip to content

[Form] Removed jQuery in favour of vanilla Javascript #17962

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

Merged
merged 1 commit into from
Mar 1, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 0 additions & 108 deletions reference/forms/types/collection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,114 +86,6 @@ existing addresses. Adding new addresses is possible by using the `allow_add`_
option (and optionally the `prototype`_ option) (see example below). Removing
emails from the ``emails`` array is possible with the `allow_delete`_ option.

Adding and Removing Items
~~~~~~~~~~~~~~~~~~~~~~~~~

If `allow_add`_ is set to ``true``, then if any unrecognized items are submitted,
they'll be added seamlessly to the array of items. This is great in theory,
but takes a little bit more effort in practice to get the client-side JavaScript
correct.

Following along with the previous example, suppose you start with two
emails in the ``emails`` data array. In that case, two input fields will
be rendered that will look something like this (depending on the name of
your form):

.. code-block:: html

<input type="email" id="form_emails_0" name="form[emails][0]" value="[email protected]"/>
<input type="email" id="form_emails_1" name="form[emails][1]" value="[email protected]"/>

To allow your user to add another email, just set `allow_add`_ to ``true``
and - via JavaScript - render another field with the name ``form[emails][2]``
(and so on for more and more fields).

To help make this easier, setting the `prototype`_ option to ``true`` allows
you to render a "template" field, which you can then use in your JavaScript
to help you dynamically create these new fields. A rendered prototype field
will look like this:

.. code-block:: html

<input type="email"
id="form_emails___name__"
name="form[emails][__name__]"
value=""
/>

By replacing ``__name__`` with some unique value (e.g. ``2``),
you can build and insert new HTML fields into your form.

Using jQuery, a simple example might look like this. If you're rendering
your collection fields all at once (e.g. ``form_row(form.emails)``), then
things are even easier because the ``data-prototype`` attribute is rendered
automatically for you (with a slight difference - see note below) and all
you need is this JavaScript code:

.. code-block:: javascript

// add-collection-widget.js
jQuery(document).ready(function () {
jQuery('.add-another-collection-widget').click(function (e) {
var list = jQuery(jQuery(this).attr('data-list-selector'));
// Try to find the counter of the list or use the length of the list
var counter = list.data('widget-counter') || list.children().length;

// grab the prototype template
var newWidget = list.attr('data-prototype');
// replace the "__name__" used in the id and name of the prototype
// with a number that's unique to your emails
// end name attribute looks like name="contact[emails][2]"
newWidget = newWidget.replace(/__name__/g, counter);
// Increase the counter
counter++;
// And store it, the length cannot be used if deleting widgets is allowed
list.data('widget-counter', counter);

// create a new list element and add it to the list
var newElem = jQuery(list.attr('data-widget-tags')).html(newWidget);
newElem.appendTo(list);
});
});

And update the template as follows:

.. code-block:: html+twig

{{ form_start(form) }}
{# ... #}

{# store the prototype on the data-prototype attribute #}
<ul id="email-fields-list"
data-prototype="{{ form_widget(form.emails.vars.prototype)|e }}"
data-widget-tags="{{ '<li></li>'|e }}"
data-widget-counter="{{ form.emails|length }}">
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>

<button type="button"
class="add-another-collection-widget"
data-list-selector="#email-fields-list">Add another email</button>

{# ... #}
{{ form_end(form) }}

<script src="add-collection-widget.js"></script>

.. tip::

If you're rendering the entire collection at once, then the prototype
is automatically available on the ``data-prototype`` attribute of the
element (e.g. ``div`` or ``table``) that surrounds your collection.
The only difference is that the entire "form row" is rendered for you,
meaning you wouldn't have to wrap it in any container element as it
was done above.

Field Options
-------------

Expand Down