0

I have a custom form with a date field and a series of textfields and number fields. The textfields and number fields are dynamically re-created via an ajax callback function that runs whenever the date field changes, because the default values of the textfields and number fields will depend on the value of the date field.

The form runs this function once upon loading, which works fine, it returns the $form object with all expected values in the fields.

However, whenever that same function is called again by the ajax trigger, almost all of the attributes of the dynamic fields are missing. The data-drupal-selector, id, name, and value attributes are gone. The default values do not appear. The for attribute of labels also disappears.

I have noticed that this is not true of table form API elements and only seems to affect input elements and labels.

Is this the expected behaviour? Is it avoidable?

Example:

public function buildForm(array $form, FormStateInterface $form_state) {
    $form['date'] = array(
        '#type' => 'date',
        '#ajax' => [
            'callback' => '::dateCallback',
            'wrapper' => 'wrapper',
        ],
        '#default_value' => date("Y-m-d")
    );
    $form['wrapper'] = [
        '#type' => 'container',
        '#attributes' => ['id' => 'wrapper'],
    ];
    $this->dateCallback($form, $form_state);
    return $form;
}

/**
 * Implements callback for Ajax event on date selection.
 *
 * @param array $form
 *   From render array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Current state of form.
 *
 * @return array
 *   Date selection section of the form.
 */
public function dateCallback(array &$form, FormStateInterface $form_state) {
    //insert logic to determine value of a variable called $field1    

    $form['wrapper']['options'] = array(
        '#type' => 'fieldset',
        '#attributes' => array('class' => array('options'))
    );
    $form['wrapper']['options']['field1'] = array(
        '#type' => 'textfield',
        '#title' => "Field 1",
        '#default_value' => $field1
    );
    $form['wrapper']['table'] = array(
        '#type' => 'table',
        '#title' => t('Table'),
        '#header' => array("H1", "H2", "H3"),
        '#attributes' => array('class' => array('custom-table'))
    );
    //insert logic to generate table rows
    return $form['wrapper'];
}

(Note: In order to get ajax to work with date fields, as per my previous question, I had to apply a Drupal patch. However, I am pretty sure this issue is not related to this patch, as the problem persists even if I attach the callback to another form APi element like a Select dropdown.)

1 Answer 1

1

It seems like a good idea to invoke the callback in the form build to include the form elements of the callback in $form to allow the form builder to process these elements. But then you overwrite the same form elements that the form builder has processed with the unprocessed form elements when the ajax triggers the callback.

Solution: Move all form elements in buildForm(). Leave the callback empty, only return the wrapper part of the form.

10
  • Thanks, I'm giving this a try. So do you mean I should move all my field definitions to buildForm()? If so, should I keep the logic that determines the value of the field in the callback function? I'm not sure where else I would put it, but having $form['wrapper']['options']['field1']['#default_value'] = $field1; in the callback does not seem to change the value now.
    – saramm1
    Commented May 23, 2017 at 19:51
  • Put everything in buildForm(), determine the triggering element with $form_state->getTriggeringElement(), if you need to react on ajax actions, don't do this in the callback.
    – 4uk4
    Commented May 23, 2017 at 19:54
  • Thanks, I see what you mean. I moved everything to buildForm() and it is working as expected. My only problem now is that it always thinks the value of the date element is null.
    – saramm1
    Commented May 23, 2017 at 20:06
  • Just read the previous question. For testing or as workaround you could place a button beside the date field to trigger the ajax.
    – 4uk4
    Commented May 23, 2017 at 20:40
  • Hmm, none of my code which depended on the date seems to be working at all now, not even the table code. The buildForm() function can't seem to detect the $form_state value of any field defined within that same function.
    – saramm1
    Commented May 24, 2017 at 14:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.