Problem/Motivation

Currently it is not possible to target HTML classes to replace with AJAX, an ID must be used.

This sucks because the Form API goes to great lengths to make HTML IDs unique, which is why data-drupal-selector was introduced back in 2015 when dinosaurs roamed the Earth (see https://www.drupal.org/node/2503277). Therefore, any element which you want to target with AJAX usually needs to be forcibly wrapped with markup to ensure an unchanging ID:

$element['#prefix'] = '<div id="ajax-target">';
$element['#suffix'] = '</div>';

Ugh. Besides being smelly code, the code which forcibly adds '#' to the wrapper string actually has a @todo to improve this situation. Let's do what it says, and make the AJAX targeting a little bit more flexible.

Proposed resolution

Leave #ajax['wrapper'] for BC, but introduce #ajax['selector'] instead.
Before:

'wrapper' => 'some-string',

After:

'wrapper_selector' => '#some-string',

Remaining tasks

N/A

User interface changes

N/A

API changes

#ajax['wrapper'] is deprecated, but has BC.

Data model changes

N/A

CommentFileSizeAuthor
#76 interdiff-74_76.txt1.08 KBgauravvvv
#76 2821793-76.patch42.49 KBgauravvvv
#75 2821793-75.patch42.49 KBanchal_gupta
#75 interdiff-2821793-74_75.txt635 bytesanchal_gupta
#74 2821793-74.patch42.49 KBgauravvvv
#72 2821793-nr-bot.txt157 bytesneeds-review-queue-bot
#62 2821793-ajax_wrapper-62-interdiff.txt4.21 KBtim.plunkett
#62 2821793-ajax_wrapper-62.patch47.56 KBtim.plunkett
#60 2821793-ajax_wrapper-60.patch44.64 KBtim.plunkett
#53 2821793-ajax_wrapper-53.patch43.86 KBtim.plunkett
#48 2821793-ajax_wrapper-48-interdiff.txt3.9 KBtim.plunkett
#48 2821793-ajax_wrapper-48.patch42.86 KBtim.plunkett
#45 2821793-ajax_wrapper-41.patch42.03 KBtim.plunkett
#42 2821793-ajax_wrapper-42.patch41.35 KBpancho
#42 2821793_41-42_interdiff.txt33.52 KBpancho
#41 2821793-ajax_wrapper-41.patch42.03 KBtim.plunkett
#40 2821793-ajax_wrapper-40-interdiff.txt617 bytestim.plunkett
#40 2821793-ajax_wrapper-40.patch42.23 KBtim.plunkett
#38 2821793-ajax_wrapper-38-interdiff.txt1.82 KBtim.plunkett
#38 2821793-ajax_wrapper-38.patch42.22 KBtim.plunkett
#36 2821793-ajax_wrapper-36-interdiff.txt7.88 KBtim.plunkett
#36 2821793-ajax_wrapper-36.patch42.07 KBtim.plunkett
#28 2821793-ajax_wrapper-28.patch35.46 KBtim.plunkett
#26 2821793-ajax_wrapper-26.patch29.73 KBtim.plunkett
#26 2821793-ajax_wrapper-26-interdiff.txt752 bytestim.plunkett
#24 2821793-ajax_wrapper-24.patch29.72 KBtim.plunkett
#24 2821793-ajax_wrapper-24-interdiff.txt1.48 KBtim.plunkett
#16 replace-#ajax-wrapper-with-ajax-wrapper_selector-2821793-11.patch13.46 KBRajender Rajan
#13 replace-#ajax-wrapper-with-ajax-wrapper_selector-2821793-10.patch14.09 KBRajender Rajan
#9 replace-#ajax-wrapper-with-ajax-wrapper_selector-2821793-9.patch14.67 KBRajender Rajan
#6 replace-#ajax-wrapper-with-ajax-wrapper_selector-2821793-6.patch16.58 KBRajender Rajan
#4 2821793-ajax_wrapper-4.patch29.93 KBtim.plunkett
#2 2821793-ajax_wrapper-2.patch29.28 KBtim.plunkett

Comments

tim.plunkett created an issue. See original summary.

tim.plunkett’s picture

Status: Active » Needs review
StatusFileSize
new29.28 KB

Everything in this is a straight conversion, except \Drupal\views_ui\ViewPreviewForm::actions() which is updated to use a class instead of an ID. All CSS is already targeting the class.

tim.plunkett’s picture

Title: Replace #ajax['wrapper'] with #ajax['selector'] » Replace #ajax['wrapper'] with #ajax['wrapper_selector']
Status: Needs work » Needs review
StatusFileSize
new29.93 KB

element_settings.selector is already used for other purposes.

Rajender Rajan’s picture

Rajender Rajan’s picture

Status: Needs work » Needs review
Rajender Rajan’s picture

Rajender Rajan’s picture

Status: Needs work » Needs review
tim.plunkett’s picture

Why are you removing necessary changes?

Rajender Rajan’s picture

Rajender Rajan’s picture

Status: Needs work » Needs review
Rajender Rajan’s picture

Rajender Rajan’s picture

Status: Needs work » Needs review
xjm’s picture

Removing issue credit for @Rajender Rajan since these patches are incorrect and do not advance the issue.

@Rajender Rajan, please post an explanation in the comment field and an interdiff when you update core patches, so that your contributions help move the Drupal core issues forward. You can also get help in IRC if you are not sure how to contribute. Thanks for your interest in working on Drupal core!

Rajender Rajan’s picture

I am unable to understand error ! I thought i have done wrong changes that why its been showing error with each patch of mine !!!

tim.plunkett’s picture

All of your patches are invalid, so they will not apply.

But more importantly, why are you rolling patches that remove my changes?

Rajender Rajan’s picture

My patches invalid in what sense ? Can u please explain bit more ?

tim.plunkett’s picture

The patches you are uploading are invalid

 The patch file /var/lib/drupalci/web/jenkins-default-246184/./replace-#ajax-wrapper-with-ajax-wrapper_selector-2821793-11.patch is invalid 
https://dispatcher.drupalci.org/job/default/246184/console

I don't know how else to explain that part.

But more importantly, why are you deleting necessary parts of my changes?

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new1.48 KB
new29.72 KB

Here is a working patch with an interdiff against my last patch.

tim.plunkett’s picture

Classes are not always alone, so must use contains.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tim.plunkett’s picture

StatusFileSize
new35.46 KB

Rerolled.

Sacrilegious’s picture

Status: Needs review » Needs work
diff --git a/core/core.api.php b/core/core.api.php
index 7c4ebf4..d79ba36 100644
--- a/core/core.api.php
+++ b/core/core.api.php
@@ -2361,16 +2361,16 @@ function hook_validation_constraint_alter(array &$definitions) {
  *   Ajax event. More information on callbacks is below in @ref sub_callback.
  * - wrapper: The HTML 'id' attribute of the area where the content returned by
  *   the callback should be placed. Note that callbacks have a choice of
- *   returning content or JavaScript commands; 'wrapper' is used for content
+ *   returning content or JavaScript commands; 'wrapper_selector' is used for content
  *   returns.

Docs aren't fully updated. Property 'wrapper' is still listed as an option and 'wrapper_selector' isn't.

diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 975d95b..9fa39ff 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -1306,10 +1306,16 @@ protected function drupalProcessAjaxResponse($content, array $ajax_response, arr
 
         case 'insert':
           $wrapperNode = NULL;
-          // When a command doesn't specify a selector, use the
-          // #ajax['wrapper'] which is always an HTML ID.
+          // When a command doesn't specify a selector, use #ajax['wrapper_selector'].
           if (!isset($command['selector'])) {
-            $wrapperNode = $xpath->query('//*[@id="' . $ajax_settings['wrapper'] . '"]')->item(0);
+            $selector_type = substr($ajax_settings['wrapper_selector'], 0, 1);
+            $selector = substr($ajax_settings['wrapper_selector'], 1);
+            if ($selector_type === '#') {
+              $wrapperNode = $xpath->query('//*[@id="' . $selector . '"]')->item(0);
+            }
+            else {
+              $wrapperNode = $xpath->query('//*[contains(@class, ' . $selector . ')]')->item(0);
+            }
           }

XPath query would fail in other case. E.g. if selector is 'textarea' or '[type="radio"]' or even ':empty'.

@@ -164,7 +164,7 @@ function views_ui_add_ajax_wrapper($element, FormStateInterface $form_state) {
 
   // The HTML ID that AJAX expects was also stored in a property on the
   // element, so use that information to insert the wrapper <div> here.
-  $id = $element['#views_ui_ajax_data']['wrapper'];
+  $id = substr($element['#views_ui_ajax_data']['wrapper_selector'], 1);
   $refresh_element += [
     '#prefix' => '',
     '#suffix' => '',

Can end up in invalid id because not only class (.) and id (#) selectors are valid 'wrapper_selector' values.

dinesh18’s picture

@@ -327,6 +327,12 @@ public static function preRenderAjaxForm($element) {
 
       $settings = $element['#ajax'];
 
+      // @todo Remove support for 'wrapper' and solely use 'wrapper_selector'.
+      if (isset($settings['wrapper'])) {
+        $settings['wrapper_selector'] = '#' . $settings['wrapper'];
+        unset($settings['wrapper']);
+      }
+

It should be if (isset($settings['wrapper_selector'])) and not if (isset($settings['wrapper'])) .

     if (this.wrapper) {
 
       /**
+       * @deprecated Use this.wrapper_selector.
+       *
        * @type {string}
        */
-      this.wrapper = '#' + this.wrapper;
+      this.wrapper = null;
+
+      /**
+       * @type {string}
+       */
+      this.wrapper_selector = '#' + this.wrapper;
     }

if (this.wrapper) should be replaced by if (this.wrapper_selector).
this.wrapper = null ----> this line should be removed

tim.plunkett’s picture

@Dinesh18 no, both of those lines exist for backwards compatibility. They're fine as is

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tim.plunkett’s picture

Issue tags: +JavaScript

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new42.07 KB
new7.88 KB

Rerolled, added a proper trigger_error for the deprecation, and fixed new occurrences.

Status: Needs review » Needs work

The last submitted patch, 36: 2821793-ajax_wrapper-36.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new42.22 KB
new1.82 KB

Fixed the JS BC layer, and added @legacy to a test explicitly testing the old behavior.

Status: Needs review » Needs work

The last submitted patch, 38: 2821793-ajax_wrapper-38.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new42.23 KB
new617 bytes

Whoops, that's @group legacy, not @legacy

tim.plunkett’s picture

Issue summary: View changes
StatusFileSize
new42.03 KB

Rerolled

pancho’s picture

StatusFileSize
new33.52 KB
new41.35 KB

Nice, however 'wrapper_selector' is a horrible name... :/

Seems like the collision anticipated in #4 didn't turn out to exist / be a problem, otherwise eschewing to 'wrapper_selector' would have fixed at least one of the #3 test failures.

Let's see if patch #41, with 'wrapper_selector' renamed back to 'selector', tests green as well. No other changes in here.

tim.plunkett’s picture

If that patch passes it only means there is missing test coverage for ajax.js.

Within the constructor for Drupal.ajax it provides a default value for selector of `#${base}` and defers to elementSettings.
That conflicts with the new selector you are adding here.
Which I pointed out in #4 over 2 years ago.

Status: Needs review » Needs work

The last submitted patch, 42: 2821793-ajax_wrapper-42.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new42.03 KB

Reuploading #41 again for the bot.

pancho’s picture

You were right, and the tests failed, too. Please ignore #42.

Let's however figure out if there's another way to have a concise property key.

pancho’s picture

I weighed #ajax['target'] as an alternative, but in the end, 'selector' is better, plus is used in AJAX commands, so should be used here, too. Let's see if we can fix that collision differently.

tim.plunkett’s picture

selector is already used and it is not going away.
Adjusting the docs.

pancho’s picture

OK, unsubscribing.

lauriii’s picture

It would be good to get sign off from a framework manager on this API change since changing 'wrapper' into 'wrapper_selector' is conceptually a big change. Using id as a selector ensures that the wrapper element is always unique. Since that is not the case after this change, I assume part of the scope is to allow returning content into multiple different wrapper elements from a single #ajax callback. We should document that in the issue summary explicitly since it is not a minor change. We should also add test coverage specifically for that.

lauriii’s picture

I tagged this for FM review because I accidentally thought there was no subsystem maintainer for ajax system. Moving this to the subsystem maintainer queue instead for addressing feedback in #50.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tim.plunkett’s picture

Issue tags: +Needs issue summary update
StatusFileSize
new43.86 KB

I've been rerolling this patch for so long I forget why I started it in the first place.
The issue summary needs to explain *why* this should be fixed, and address the implications of what happens when using a non-unique selector.

phenaproxima’s picture

Issue summary: View changes

Improved the problem/motivation section (I hope).

phenaproxima’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Discussed this a bit with @tim.plunkett in Slack, trying to figure out what to do about the "multiple targeted elements" question. I felt that we should hard-code the system to only work on the first targeted element (.eq(0)), at least for now; Tim felt that we should allow multiple targets, and add test coverage. Since he is one of the maintainers of this subsystem, I think his opinion probably carries more weight than mine :) Tim also pointed out that nothing in core is currently taking advantage of multiple targeting, so things will continue to work the way they already do. In other words, although this change will expand the AJAX system's capabilities, it won't break existing code.

So, okay. Multiple targeting it is. I'm tagging this issue for more tests and kicking it back for that.

berdir’s picture

One reason IMHO is that ids are, at least in certain places, dynamically altered, and if you're not extra careful, then ajax replacements no longer work the second time.

What I always wondered is why we added those data-drupal-selector attributes but afaik not use it anywhere (there might be an issue somewhere). You could also use the new selector to target these, probably just a bit unwieldy.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

mglaman’s picture

One reason you would want to use this is if you are replacing the entire form on AJAX. You cannot keep replacing the form unless you ensure a stable ID.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

tim.plunkett’s picture

Status: Needs work » Needs review
Issue tags: -JavaScript +JavaScript
StatusFileSize
new44.64 KB

Status: Needs review » Needs work

The last submitted patch, 60: 2821793-ajax_wrapper-60.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

tim.plunkett’s picture

Status: Needs work » Needs review
StatusFileSize
new47.56 KB
new4.21 KB
anu.a_95’s picture

Assigned: Unassigned » anu.a_95
anu.a_95’s picture

anu.a_95’s picture

Assigned: anu.a_95 » Unassigned

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

tanubansal’s picture

Tested #62 on 9.1, Replace #ajax['wrapper'] with #ajax['wrapper_selector'] successfully applied

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new157 bytes

The Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

solideogloria’s picture

I think this issue is a good idea. If you want to embed a form in a block, and that block occurs more than one time on the same page, then you have an ID conflict due to hardcoding the wrapper ID. I don't know of any workaround as-is.

gauravvvv’s picture

Status: Needs work » Needs review
StatusFileSize
new42.49 KB

Patch #62, no longer applies to 10.1.x. I have re-rolled the patch #62, for 10.1.x. Please review

There is size difference with the patch #62, as es6.js files are removed from the current patch.

anchal_gupta’s picture

StatusFileSize
new635 bytes
new42.49 KB

Fixed CCF.

gauravvvv’s picture

StatusFileSize
new42.49 KB
new1.08 KB

Fixed CCF, attached interdiff with #74.

smustgrave’s picture

Status: Needs review » Needs work

Deprecation needs updating.

Also this was tagged for issue summary update 4 years ago. is #55 good enough?

Proposed solution could be cleaned up I think.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

fathershawn’s picture

Status: Needs work » Closed (won't fix)

We are on a path to deprecate Ajax API. New functionality is now out of scope, however the new subsystem accepts any valid CSS selector by design. See #3404409: [Plan] Gradually replace Drupal's AJAX system with HTMX.

catch’s picture

Status: Closed (won't fix) » Postponed

Usually for issues like this that we're hoping to make irrelevant, I move them to postponed on the issue/meta that will do that. That way there's a way to come back and point people to the new way when it's done, while still discouraging work on something that's unlikely to get reviewed/committed before it's removed anyway.

Marking postponed on #3404409: [Plan] Gradually replace Drupal's AJAX system with HTMX for now