Fixed #33328 -- Transformed formset:added/removed to native JS events.

This commit is contained in:
Claude Paroz 2022-02-23 10:33:07 +01:00 committed by GitHub
parent 1f42a352e0
commit eabc22f919
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 66 deletions

View File

@ -27,9 +27,7 @@
$('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2(); $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2();
}); });
$(document).on('formset:added', (function() { document.addEventListener('formset:added', (event) => {
return function(event, $newFormset) { $(event.target).find('.admin-autocomplete').djangoAdminSelect2();
return $newFormset.find('.admin-autocomplete').djangoAdminSelect2(); });
};
})(this));
} }

View File

@ -88,7 +88,12 @@
if (options.added) { if (options.added) {
options.added(row); options.added(row);
} }
$(document).trigger('formset:added', [row, options.prefix]); row.get(0).dispatchEvent(new CustomEvent("formset:added", {
bubbles: true,
detail: {
formsetName: options.prefix
}
}));
}; };
/** /**
@ -130,7 +135,11 @@
if (options.removed) { if (options.removed) {
options.removed(row); options.removed(row);
} }
$(document).trigger('formset:removed', [row, options.prefix]); document.dispatchEvent(new CustomEvent("formset:removed", {
detail: {
formsetName: options.prefix
}
}));
// Update the TOTAL_FORMS form count. // Update the TOTAL_FORMS form count.
const forms = $("." + options.formCssClass); const forms = $("." + options.formCssClass);
$("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length); $("#id_" + options.prefix + "-TOTAL_FORMS").val(forms.length);

View File

@ -8,15 +8,15 @@ Inline form events
================== ==================
You may want to execute some JavaScript when an inline form is added or removed You may want to execute some JavaScript when an inline form is added or removed
in the admin change form. The ``formset:added`` and ``formset:removed`` jQuery in the admin change form. The ``formset:added`` and ``formset:removed`` events
events allow this. The event handler is passed three arguments: allow this. ``event.detail.formsetName`` is the formset the row belongs to.
For the ``formset:added`` event, ``event.target`` is the newly added row.
* ``event`` is the ``jQuery`` event. .. versionchanged:: 4.1
* ``$row`` is the newly added (or removed) row.
* ``formsetName`` is the formset the row belongs to.
The event is fired using the :ref:`django.jQuery namespace In older versions, the event was a ``jQuery`` event with ``$row`` and
<contrib-admin-jquery>`. ``formsetName`` parameters. It is now a JavaScript ``CustomEvent`` with
parameters set in ``event.detail``.
In your custom ``change_form.html`` template, extend the In your custom ``change_form.html`` template, extend the
``admin_change_form_document_ready`` block and add the event listener code: ``admin_change_form_document_ready`` block and add the event listener code:
@ -34,17 +34,14 @@ In your custom ``change_form.html`` template, extend the
.. code-block:: javascript .. code-block:: javascript
:caption: app/static/app/formset_handlers.js :caption: app/static/app/formset_handlers.js
(function($) { document.addEventListener('formset:added', (event) => {
$(document).on('formset:added', function(event, $row, formsetName) { if (event.detail.formsetName == 'author_set') {
if (formsetName == 'author_set') {
// Do something // Do something
} }
}); });
document.addEventListener('formset:removed', (event) => {
$(document).on('formset:removed', function(event, $row, formsetName) {
// Row removed // Row removed
}); });
})(django.jQuery);
Two points to keep in mind: Two points to keep in mind:
@ -53,29 +50,3 @@ Two points to keep in mind:
* ``{{ block.super }}`` is added because Django's * ``{{ block.super }}`` is added because Django's
``admin_change_form_document_ready`` block contains JavaScript code to handle ``admin_change_form_document_ready`` block contains JavaScript code to handle
various operations in the change form and we need that to be rendered too. various operations in the change form and we need that to be rendered too.
Sometimes you'll need to work with ``jQuery`` plugins that are not registered
in the ``django.jQuery`` namespace. To do that, change how the code listens for
events. Instead of wrapping the listener in the ``django.jQuery`` namespace,
listen to the event triggered from there. For example:
.. code-block:: html+django
{% extends 'admin/change_form.html' %}
{% load static %}
{% block admin_change_form_document_ready %}
{{ block.super }}
<script src="{% static 'app/unregistered_handlers.js' %}"></script>
{% endblock %}
.. code-block:: javascript
:caption: app/static/app/unregistered_handlers.js
django.jQuery(document).on('formset:added', function(event, $row, formsetName) {
// Row added
});
django.jQuery(document).on('formset:removed', function(event, $row, formsetName) {
// Row removed
});

View File

@ -412,6 +412,10 @@ Miscellaneous
* The ``type="text/css"`` attribute is no longer included in ``<link>`` tags * The ``type="text/css"`` attribute is no longer included in ``<link>`` tags
for CSS :doc:`form media </topics/forms/media>`. for CSS :doc:`form media </topics/forms/media>`.
* ``formset:added`` and ``formset:removed`` JavaScript events are now pure
JavaScript events and don't depend on jQuery. See
:ref:`admin-javascript-inline-form-events` for more details on the change.
.. _deprecated-features-4.1: .. _deprecated-features-4.1:
Features deprecated in 4.1 Features deprecated in 4.1

View File

@ -38,26 +38,20 @@ QUnit.test('added form has remove button', function(assert) {
}); });
QUnit.test('add/remove form events', function(assert) { QUnit.test('add/remove form events', function(assert) {
assert.expect(6); assert.expect(5);
const $ = django.jQuery;
const $document = $(document);
const addButton = this.table.find('.add-row a'); const addButton = this.table.find('.add-row a');
$document.on('formset:added', function(event, $row, formsetName) { document.addEventListener('formset:added', (event) => {
assert.ok(true, 'event `formset:added` triggered'); assert.ok(true, 'event `formset:added` triggered');
assert.equal(true, $row.is('#first-1')); assert.equal(true, event.target.matches('#first-1'));
assert.equal(formsetName, 'first'); assert.equal(event.detail.formsetName, 'first');
$document.off('formset:added'); }, {once: true});
});
addButton.click(); addButton.click();
const deletedRow = $('#first-1');
const deleteLink = this.table.find('.inline-deletelink'); const deleteLink = this.table.find('.inline-deletelink');
$document.on('formset:removed', function(event, $row, formsetName) { document.addEventListener('formset:removed', (event) => {
assert.ok(true, 'event `formset:removed` triggered'); assert.ok(true, 'event `formset:removed` triggered');
assert.equal(true, $row.is(deletedRow)); assert.equal(event.detail.formsetName, 'first');
assert.equal(formsetName, 'first'); }, {once: true});
$document.off('formset:removed'); deleteLink.click();
});
deleteLink.trigger($.Event('click', {target: deleteLink}));
}); });
QUnit.test('existing add button', function(assert) { QUnit.test('existing add button', function(assert) {