1

I have a form that contains two fieldsets. In the fieldsets, I have a hidden field $attr_flag which stores a 1 or 0 to determine whether the fieldset should be colored gray with css, a text field and a button with AJAX that changes the value of the $attr_flag. When clicking the buttons inside the fieldsets, the $attr_flag should switch between 0 and 1 and also add/remove a gray background for the fieldset.

It works as expected when I click one button over and over, however if I click button 1 then button 2, there is a 'Please wait...' response from button 2 but the fieldset background color does not change. If I click button 2 multiple times, it will start changing fieldset 1's background, so it thinks it is refreshing fieldset 1.

There are no errors in the DB Log nor in the Chrome console. The code is below, I'm at a loss for what to do here, any help is greatly appreciated!

public function buildForm(array $form, FormStateInterface $form_state, $input=NULL, $context=NULL) {

  $num_obj = 2;
  $init_load = TRUE;
  if($form_state->isSubmitted()){
    $init_load = FALSE;
  }

  //If this is the first time the form is loaded (not AJAX)
  if($init_load){

    $form['#tree'] = TRUE;
    $form['actions'] = ['#type' => 'actions',];
    $form['num_obj'] = [
      '#type' => 'hidden',
      '#default_value' => $num_obj,
    ];     

    //Build fieldset for each object
    for($attr = 1; $attr <= $num_obj; $attr++){

      $attr_fieldset = 'attr_' . $attr;
      $attr_wrapper = $attr_fieldset . "_wrapper";
      $attr_flag = 'flag_' . $attr;
      $attr_name = 'name_' . $attr;
      $attr_flag_button = 'flag_button_' . $attr;

      $form[$attr_fieldset] = [
        '#type' => 'fieldset',
        '#prefix' => '<div id="' . $attr_wrapper . '">',
        '#suffix' => '</div>',   
      ];
      $form[$attr_fieldset][$attr_flag] = [
        '#type' => 'hidden',
        '#value' => 0,
      ];
      $form[$attr_fieldset][$attr_name] = [
        '#type' => 'textfield',
        '#title' => 'Name',
      ];
      $form[$attr_fieldset]['actions'][$attr_flag_button] = [
        '#type' => 'submit',
        //'#name' => $attr_flag_button,
        //'#value' => 'Flag',
        '#value' => 'Flag ' . $attr,
        '#submit' => ['::removeUndoCallback'],
        '#ajax' => [
          'callback' => [$this, 'Drupal\data_mgmt\Form\ObjectForm::ajaxCallback'],
          'wrapper' => $attr_wrapper,
        ],
      ];
    }
  }//if init load

  //Else this is an AJAX reload
  else{

    //Get the button clicked and generate all fields to rebuild form
    //$trg_elem = $form_state->getTriggeringElement()['#name'];
    //$attr = str_replace('flag_button_', '', $trg_elem);
    $trg_elem = $form_state->getTriggeringElement()['#value'];
    $attr = str_replace('Flag ', '', str_replace('Un-flag ', '', $trg_elem));
    $attr_fieldset = 'attr_' . $attr;
    $attr_wrapper = $attr_fieldset . "_wrapper";
    $attr_flag = 'flag_' . $attr;
    $attr_name = 'name_' . $attr;
    $attr_flag_button = 'flag_button_' . $attr;
    $flag_val = $form_state->getValue([$attr_fieldset,$attr_flag]);  
    //$flag_text = 'Flag';    
    //if($flag_val == 1){
    //  $flag_text = 'Un-flag';
    //}
    $flag_text = 'Flag ' . $attr;    
    if($flag_val == 1){
      $flag_text = 'Un-flag ' . $attr;
    }

    $form[$attr_fieldset] = [
      '#type' => 'fieldset',
      '#prefix' => '<div id="' . $attr_wrapper . '">',
      '#suffix' => '</div>',   
    ];
    $form[$attr_fieldset][$attr_flag] = [
      '#type' => 'hidden',
      '#value' => $flag_val,
    ];
    $form[$attr_fieldset][$attr_name] = [
      '#type' => 'textfield',
      '#title' => 'Name',
    ];
    $form[$attr_fieldset]['actions'][$attr_flag_button] = [
      '#type' => 'submit',
      //'#name' => $attr_flag_button,
      '#value' => $flag_text,
      '#submit' => ['::removeUndoCallback'],
      '#ajax' => [
        'callback' => [$this, 'Drupal\data_mgmt\Form\ObjectForm::ajaxCallback'],
        'wrapper' => $attr_wrapper,
      ],
    ];

    if($flag_val == 1){
      $form[$attr_fieldset]['#attributes'] = array('style' => 'background-color: gray');
    }
  }

  $form_state->setCached(FALSE);

  $form['actions']['submit'] = [
    '#type' => 'submit',
    '#value' => $this->t('Save'),
  ];

  return $form;


}

public function ajaxCallback(array &$form, FormStateInterface $form_state) {

  //Get the clicked button name and return that fieldset
  //$trg_elem = $form_state->getTriggeringElement()['#name'];
  //if($trg_elem == "flag_button_1"){
  $trg_elem = $form_state->getTriggeringElement()['#value'];
  if(strpos($trg_elem, '1')){
    return $form['attr_1'];
  }
  else{
    return $form['attr_2'];
  }

}

public function removeUndoCallback(array &$form, FormStateInterface $form_state) {

  //Get the clicked button name, change the flag status between 0 and 1
  //$trg_elem = $form_state->getTriggeringElement()['#name'];
  //$attr = str_replace('flag_button_', '', $trg_elem);
  $trg_elem = $form_state->getTriggeringElement()['#value'];
  $attr = str_replace('Flag ', '', str_replace('Un-flag ', '', $trg_elem));
  $attr_fieldset = 'attr_' . $attr;
  $attr_flag = 'flag_' . $attr;
  $flag_val = $form_state->getValue($attr_flag);

  if($flag_val == 1){
    $form_state->setValue([$attr_fieldset, $attr_flag], 0); 
  }
  else{
    $form_state->setValue([$attr_fieldset, $attr_flag], 1);
  }

  $form_state->setRebuild();

}
  • Try removing the #name from the submit buttons, as this can cause issues on submit buttons, and give each of the submit buttons different text (#value) and see what happens. – Jaypan Apr 28 '18 at 22:48
  • Thanks for getting back to me Jaypan. I tried this but it is still not working. I can click button 1 a few times and it works fine, but once I start clicking button 2, it gets re-named to 1 and changes fieldset 1. The changes I made specifically: removed '#name' from buttons, added a space then number (1,2) to the button values, 'Flag 1' / 'Un-flag 1' and 'Flag 2' / 'Un-flag 2'. Then in the buildForm, ajaxCallback, and removeUndoCallback I check the #value instead of #name to determine what changes to make. – Mike Apr 29 '18 at 3:10
  • Jaypan, also I updated the code to reflect these changes, I commented out the old so you can see what's changed. – Mike Apr 29 '18 at 3:26
  • I'm thinking the problem here is in how you are defining two entirely different forms depending upon the ajax submission. the form should be one definition regardless, with changes being made to the elements in the form on form submission. You will then only return the relevant parts in your ajax callback. – Jaypan Apr 29 '18 at 4:16
  • What do you mean I am defining two entirely different forms? Are you saying that the fieldsets are different forms? Also, my understanding is that I am returning only the relevant fieldset in the callback. Then changing the form value in the submission function and rebuilding that form fieldset in buildForm. Please correct me if I am wrong, thanks! – Mike Apr 29 '18 at 12:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.