Follow-up to #2844261: Allow dialog links to specify a renderer in addition to a dialog type

Problem/Motivation

Sometimes the Drupal.ajax.instances array contains items that are null.

It seems that when the off-canvas form is opened up in the dialog it creates an element in Drupal.ajax.instances because the forms submit via ajax now after #2785047: In Outside In mode, form validation messages should appear in the off-canvas tray, not the main page. So when the dialog is closed or reloaded this code in ajax.es6js is invoked

detach(context, settings, trigger) {
      if (trigger === 'unload') {
        Drupal.ajax.expired().forEach((instance) => {
          // Set this to null and allow garbage collection to reclaim
          // the memory.
          Drupal.ajax.instances[instance.instanceIndex] = null;
        });
      }
    },

which sets the element in the array for the form submit to NULL.

To manually confirm this error:

  1. Enable Settings Tray module
  2. Goto any front-end page.
  3. Click "Edit" in the toolbar to go into Edit Mode
  4. Click any block that will open the off-canvas dialog
  5. Close the off-canvas dialog
  6. Click the same block again to open the off-canvas dialog
  7. Check the Javascript console for your browser and confirm this error shows up

    Uncaught TypeError: Cannot read property 'element' of null
    at http://www.octo2.dev/d8_2_ux/core/modules/outside_in/js/outside_in.js?v=...

Proposed resolution

Opt out of operating on those instances to avoid throwing an error when opening the same form in settings tray multiple times in a row.

Although it would be great to have tests for this changes it does not actually produce any error except the Javascript console error that we can test for.
In theory the test should just fail because of this error but if you look at the test_only patch in #4 this is not the case.

It is possible to confirm with manually testing list above that patch in #2 does fix the problem.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drpal created an issue. See original summary.

GrandmaGlassesRopeMan’s picture

Status: Fixed » Needs review
FileSize
1.45 KB

🤷‍♀️🤷‍♀️🤷‍♀️🤷‍♀️🤷‍♀️🤷‍♀️

GrandmaGlassesRopeMan’s picture

Issue summary: View changes
tedbow’s picture

I have seen this problem also.

I think the nulls are created here in ajax.es6.js

 detach(context, settings, trigger) {
      if (trigger === 'unload') {
        Drupal.ajax.expired().forEach((instance) => {
          // Set this to null and allow garbage collection to reclaim
          // the memory.
          Drupal.ajax.instances[instance.instanceIndex] = null;
        });
      }
    },

Here is test only patch which is trying to show that error happens. But locally it won't fail for some reason. So maybe it has something to do with another core module being enabled?

tedbow’s picture

Talked with @drpal about why there would be instances of NULL in Drupal.ajax.instances.

It seems that when the form is opened up in the dialog it creates an element in Drupal.ajax.instances because the forms submit via ajax now after #2785047: In Outside In mode, form validation messages should appear in the off-canvas tray, not the main page. So we the dialog is closed or reloaded the code I referenced in #4 is invoked which sets the element in the array to NULL.

We are not sure why the element in Drupal.ajax.instances is not just removed from the array rather than set to NULL.

Not sure how to write a test that fails or why #4 isn't failing.

tedbow’s picture

Priority: Major » Normal
Issue summary: View changes
tedbow’s picture

Ok I couldn't figure out how to write test to confirm this fix... but I looked that code around loop through the Drupal.ajax.instances that we are affecting with the patch and I realized there was part in there that we aren't test for now that we could be testing for

instance.options.data.dialogOptions.outsideInActiveEditableId = $(instance.element).parents('.outside-in-editable').attr('id');

This is used later to set the class outside-in-active-editable on the block that is currently being edited in the off-canvas dialog.

We don't test for this. So I thought it would be good to add a test for this in the current patch because then we confirm that we aren't messing up current functionally.

tedbow’s picture

Created #2898242: Delete unused Drupal.ajax.instances on unload instead of settings to null as either follow up or a fix to the root cause.

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.

Wim Leers’s picture

#2898242: Delete unused Drupal.ajax.instances on unload instead of settings to null was closed, because it's working as designed — it was marked as such by @droplet who helped make it work like this. Doesn't that impact this patch too then? Nope, that just confirms that this patch makes sense.

The patch has test coverage. But its test-only patch also passes…

I think you can test it "quite easily" though:

  1. have something on the page that has #ajax => something
  2. trigger that AJAX, and make it replace the existing content, but ensure that the new content ALSO has some #ajax stuff (perhaps to replace the same thing once again)

Then step 1 has AJAX instance X associated. Step 2 triggered AJAX instance X, caused its associated element to be deleted (because replaced HTML), and also had a Settings command (for its new AJAX instance X+1). Executing the Settings command triggers a call to Drupal.ajax.expired(), which is what causes AJAX instance X to become null.

Knowing that, I think it's possible to write a test?

droplet’s picture

I opened a new issue and also patched the same place. You may interested:

#2901667: To optimize outside_in Drupal.behaviors.toggleEditMode

tim.plunkett’s picture

xjm’s picture

Priority: Normal » Major

This issue blocks two experimental modules, so promoting to major.

GrandmaGlassesRopeMan’s picture

Status: Needs work » Needs review
FileSize
3.79 KB
1.45 KB

- remove partial tests

Additionally, this is probably a problem better positioned for a unit test, which we obviously don't have for JavaScript. Since this doesn't actually blow up the rest of the JavaScript, and just throws a console error every time Drupal.behaviors.toggleEditMode.attach() is run.

tedbow’s picture

Status: Needs review » Reviewed & tested by the community

Re #10 and the need for tests.

If you look at #4 it does a very similar test. It opens the tray and closes multiple time in multiple different configurations. If you try this manually you can see you get the JS console error but actually functionality is broken.

So I am pretty our JavascriptTestBase test don't fail just because of JS console error. So am not sure to test this. Marking back as RTBC because don't have unit tests we actually can't write test for this type of error

If you see #2551373: contextual.js and quickedit.js should fail gracefully, with useful error messages, when Twig templates forget to print attributes it had the same problem, a JS console error was impossible to write a test from. We should have JS unit tests but I don't think we should postpone this issue on that.

  • effulgentsia committed b5ec4f1 on 8.5.x
    Issue #2897938 by tedbow, drpal: Prevent accessing null elements when...

  • effulgentsia committed 159a77d on 8.4.x
    Issue #2897938 by tedbow, drpal: Prevent accessing null elements when...

effulgentsia’s picture

Status: Reviewed & tested by the community » Fixed

#14 looks like a good idea and a completely safe change to make close to releasing 8.4-beta, so pushed it to 8.5.x and 8.4.x.

Looks like #2901667: To optimize outside_in Drupal.behaviors.toggleEditMode has some additional nice improvements, so reviews there would be great.

A follow-up for adding test coverage would be really good. Even if it needs to be postponed on some other issue, let's at least get the relevant issues filed. Thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

tedbow’s picture

Component: outside_in.module » settings_tray.module

Changing to new settings_tray.module component. @drpal thanks for script help! :)