Just encountered the error:

Uncaught TypeError: Cannot read property 'submitButtonSelector' of undefined

This happens when I use the Drag and Drop after validation fails. If I put the following in an after build function:

<?php
  $selector = variable_get('dragdropfile_submit_button_selector', DRAGDROPFILE_SUBMIT_BUTTON_SELECTOR);
  drupal_add_js(array('dragDropFile' => array('submitButtonSelector' => $selector)), 'setting');
?>

it does then work again after validation. Can anyone else replicate this error?

Comments

tce created an issue. See original summary.

tce’s picture

Issue summary: View changes
rudiedirkx’s picture

Which validation? JS does a simple client side validation for file extension. Which validation fails for you? I'm assuming server side. I want to reproduce.

tce’s picture

Sorry, yeah, server validation. In node edit, when I press the submit button and the form validation fails and page reloads again, I get this error and the drag and drop no longer works. However, if I use 'hook_form_alter' and put the code in an after build function it does then work after validation. I've not tested this on a vanilla Drupal install though, which I will do later today - I am using a lot of modules for the site I'm working on.

Just curious, could you not attach this to the form instead? eg:

<?php
    $selector = variable_get('dragdropfile_submit_button_selector', DRAGDROPFILE_SUBMIT_BUTTON_SELECTOR);
    $element['#attached']['js'][] = array(
      'data' => array('dragDropFile' => array('submitButtonSelector' => $selector)),
      'type' => 'setting'
    );
?>

UPDATE
=======

I created a site at https://simplytest.me/ with this module and the same problem occurs.

Step 1: Create a site with drag and drop enabled, add an image field to a content.
Step 2: Go to add content, click submit and validation should fail as no title.
Step 3: Drag a file into the box. Nothing happens but error shown in console.

My hunch is the setting is not being remembered due to form caching, so maybe attaching or adding in after_build could fix?

rudiedirkx’s picture

Now that's debugging! Thanks man. (I can't seem to get DDF working at all on SimplyTest though. It keeps telling me "The uploaded file likely exceeded the maximum file size (2 MB) that this server supports.".)

I can add more settings in #attached, but the problem is if they exist double, the JS setting will be broken too. So I have to be very sure exactly where and when to add it, so it always exist once. I thought this is why #attached existed: if the element is rendered, the attached JS is included. Maybe I still misunderstand.

Maybe tomorrow...

tce’s picture

As I understand it, dragdropfile_field_widget_form_alter() will not run again after validation fails, because the form is called from the cache at this time. So the call to drupal_add_js() to add the setting does not run causing the JS error as your JS code is expecting it.

Either of the below solutions fix the problem for me.

1) Move your drupal_add_js() code into an after_build function.

<?php
...

    // Custom submit button selector.
    $element['#after_build'][] = 'dragdropfile_attach_resources_after_build';
  }
}
?>
<?php
function dragdropfile_attach_resources_after_build($form, &$form_state) {
  $selector = variable_get('dragdropfile_submit_button_selector', DRAGDROPFILE_SUBMIT_BUTTON_SELECTOR);
  drupal_add_js(array('dragDropFile' => array('submitButtonSelector' => $selector)), 'setting');
  
  return $form;
}
?>

2) Replacing drupal_add_js() with #attached

<?php
    $selector = variable_get('dragdropfile_submit_button_selector', DRAGDROPFILE_SUBMIT_BUTTON_SELECTOR);
    $element['#attached']['js'][] = array(
      'data' => array('dragDropFile' => array('submitButtonSelector' => $selector)),
      'type' => 'setting'
    );
?>

I think the best way is using #attached.

I can add more settings in #attached, but the problem is if they exist double

I'm not sure what you mean but I think render() uses drupal_add_js() when it sees #attached anyway, so it should work the same as how you have it now, the only difference is that drupal_add_js() is eventually getting called after validation and not lost due to form caching. That's how I understand it anyway.

  • rudiedirkx committed 69acccc on 7.x-1.x
    Issue #2559763 by tce, rudiedirkx: Uncaught TypeError: Cannot read...
rudiedirkx’s picture

Ooooooooh, I wasn't using #attached for the JS settings! OMG I'm a dumbass. Yes, you're right of course, #attached is the best. Sorry that took a while =)

I'm still getting fails on validate though. I have 2 multiple drag & drop image fields. When I drag 1 image onto a field, then submit without title, it fails on validation, but all #attached JS & CSS isn't included anymore. The 1 dropped image is there, but no drop container anywhere, not even on the other (empty) field. That's weird...

That problem goes away if I remove the $form_state['#dragdropfile'] check, which' function was to add all JS & CSS only once, but apparently Drupal is smart (?) enough to do that anyway. This is the best way I think, because every separate element should require all JS, not just the first in the form build process.

Let me know if the last commit works for you. It should be referenced around here somewhere.

tce’s picture

Ah yes, I'm seeing the other bug. When validation fails, I can now (with the fix) drag an image over, but if validation fails again after that, the drag and drop container disappears.

Removing the if (empty($form_state['#dragdropfile'])) check fixes the problem. I don't think that's needed as the array that is built to add the css and js assets use the filename as the key, so there is no risk of duplicating assets. If you go to drupal_add_js() at the bottom it says:

<?php
...
      default: // 'file' and 'external'
        // Local and external files must keep their name as the associative key
        // so the same JavaScript file is not added twice.
        $javascript [$options ['data']] = $options;
...
?>

Your module works a treat now, thank you.

rudiedirkx’s picture

I knew the files wouldn't be included twice, but I was afraid the settings might be. I still don't understand why they're not. For some reason it works and I don't understand why. Or maybe they are, but the deep merge is smart enough to not make it an array...

The safer option is to attach the settings to the DOM element, maybe with a data attribute, but since this works, I guess we're done. Thanks for all the explanation and debugging!

rudiedirkx’s picture

Status: Active » Fixed

I'll create a new release soon.

tce’s picture

Oh I see. Not sure, but if I put only the settings part of the code in the check it still works:

<?php
/**
 * Helper to attach JS and CSS resources to a form.
 */
function _dragdropfile_attach_resources(&$element, &$form_state) {
  // Expose this field.
  $element['#dragdropfile'] = TRUE;
  $upload_method = variable_get('dragdropfile_upload_method', DRAGDROPFILE_UPLOAD_METHOD);

  // Add resources to this form.
  $path = drupal_get_path('module', 'dragdropfile');
  $element['#attached']['js'][] = $path . '/Drupal.ajax.prototype.eventResponse.js';
  $element['#attached']['js'][] = $path . '/dragdropfile-' . $upload_method . '.js';
  $element['#attached']['css'][] = $path . '/dragdropfile.css';
  if (variable_get('dragdropfile_cancel_all_drops', DRAGDROPFILE_CANCEL_ALL_DROPS)) {
    $element['#attached']['js'][] = $path . '/dragdropfile-lousy-aim.js';
  }
    
  // Attach global resources.
  if (empty($form_state['#dragdropfile'])) {
    $form_state['#dragdropfile'] = TRUE;

    // Custom submit button selector.
    $selector = variable_get('dragdropfile_submit_button_selector', DRAGDROPFILE_SUBMIT_BUTTON_SELECTOR);
    $element['#attached']['js'][] = array(
      'data' => array('dragDropFile' => array('submitButtonSelector' => $selector)),
      'type' => 'setting'
    );
  }
}
?>
rudiedirkx’s picture

Whaaat? That's even weirder, because that's the part it was missing before!

Screw it. This works. Don't touch it! I'm making a new release.

Status: Fixed » Closed (fixed)

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