I'm trying to form alter the user registration form on a Drupal 8 site to show different fields based on a user's selection from a checkbox. I have this working by placing all of the fields into a parent container and setting the #access on the form fields based on the form state's value for that checkbox. Then, when the checkbox changes, I use the #ajax paramater to return the new container.

The issue I'm running into is that I need to include a file upload in the registration form. My code is showing the file upload field just fine. However, when a user uploads a file, the field disappears and won't let the user remove the file. If they submit the form, the file is saved. Another nuance is that if I change the checkbox and change it back, forcing the #ajax to run again, the file will appear with the remove button. Then, clicking 'remove', will hide the field. Changing the checkbox again will allow the field to reappear.

How can I fix this so the upload field doesn't disappear after the user uploads their file?

I created a quick screencast of the issue here:
https://www.youtube.com/watch?v=h9nkIniCyz8

The form_alter code I am using in my THEME.theme file is:


/**
 * Implements hook_form_FORM_ID_alter().
 */
function THEME_form_user_register_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
    // Add a role selection field.
    $form['user_roles'] = array(
      '#type' => 'radios',
      '#options' => $new_role_options,
      '#title' => t('What type of account would you like to create'),
      '#required' => TRUE,
      '#suffix' => '<hr />',
      '#ajax' => [
        'callback' => 'THEME_get_user_roles_register_form',
        'wrapper' => 'role-wrapper',
      ],
      '#weight' => 49,
    );

    // Add a wrapping container to ajax change the field states.
    $form['role_wrapper'] = [
      '#type' => 'container',
      '#attributes' => ['id' => 'role-wrapper'],
      '#weight' => 50,
    ];

    // Get the form's children.
    $children = Element::children($form);

    // Loop through the children.
    foreach ($children as $id => $formkey) {
      $remove = FALSE;
      // If we find an employer field, add the proper #access information.
      // Employer related fields have the structure 'field_employer_NAME'
      if (strpos($formkey, 'field_employer') !== FALSE) {
        // Remove access from all field_employer fields if the role selected is
        // not 'employer'.
        $form[$formkey]['#access'] = ($form_state->getValue('user_roles') == 'employer' ? TRUE : FALSE);
        $remove = TRUE;
      }
      else if (strpos($formkey, 'field_seeker') !== FALSE) {
        // Remove access from all field_seeker fields if the role selected is
        // not 'job_seeker'.
        // Seeker related fields have the structure 'field_seeker_NAME'
        $form[$formkey]['#access'] = ($form_state->getValue('user_roles') == 'job_seeker' ? TRUE : FALSE);
        $remove = TRUE;
      }

      // Move the element to the role_wrapper container and remove the original.
      if ($remove) {
        // Set all profile fields into the role wrapper.
        $form['role_wrapper'][$formkey] = $form[$formkey];
        
        // Remove the original field.
        unset($form[$formkey]); 
      }
    } 
  }
}

/**
 * Callback function to return the role fields based on the role selected.
 */
function THEME_get_user_roles_register_form(array &$form, Drupal\Core\Form\FormStateInterface $form_state) {
  // Return the role_wrapper container.
  return $form['role_wrapper'];
}

Thanks in advance for any help you can provide.

Comments

axejko’s picture

I had similar problem, but with D7
Solution: You must to add a 2 lines into hook_menu in your page item:

'file path' => drupal_get_path('module', 'node'),
'file' => 'node.pages.inc',

More: file upload broken on node form on custom page