Score:0

Can I add elements to a field widget with AJAX?

lb flag

I want to add fields depending on the first option selected. I also made a form with a similar behaviour but it wasn't a field widget.

I used this module as an example to make the form

I'm not sure if the way I implement this AJAX behavior should be different in a widget.

Can the behavior of this form be implemented in a field widget? I'm trying to look for examples of specifically field widgets implementing this but I still haven't found anything that could help `

 public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $value = isset($items[$delta]->value) ? $items[$delta]->value : '';
    $element = [];

    // Gather the number of names in the form already.
    $num_names = $form_state->get('num_names');
    // We have to ensure that there is at least one name field.
    if ($num_names === NULL) {
        $name_field = $form_state->set('num_names', 1);
        $num_names = 1;
    } 

    $element['breakpoints'] = [
      '#type' => 'select',
      '#title' => $this->t('Breakpoint group'),
      '#options' => $this->getBreakpointsGroups(),
      '#default_value' => $value,
      '#empty_option' => $this->t('-Select a breakpoint group-'),
    ];   



    $element['images_fieldset'] = [
        '#type' => 'fieldset',
        '#title' => $this->t('Load images for each breakpoint'),
        '#prefix' => '<div id="breakpoint-wrapper">',
        '#suffix' => '</div>',
    ];

    for ($i = 0; $i < $num_names; $i++) {
        $element['images_fieldset']['image'][$i] = [
            '#type' => 'image',
            '#title' => $this->t('Image'),
            '#name' => "Name"
        ];
    }

    $element['images_fieldset']['submit'] = [
        '#type' => 'submit',
        '#title' => 'Submit',
        '#submit' => ['::submitForm'],
        '#value' => $this->t("submit"),
        '#ajax' => [
            'callback' => '::loadImageFields',
            'wrapper' => 'breakpoint-wrapper',
        ],
    ];


    return ['value' => $element];
}

/**
 * Returns a list of breakpoints.
 *
 * @return array
 *   An associative array of breakpoints, suitable to use as form
 *   options.
 */
protected function getBreakpointsGroups() {
    $breakpoints = \Drupal::service('breakpoint.manager')->getGroups();
    $breakpoint_names = array_keys($breakpoints);
    $breakpoint_group = array();
    foreach ($breakpoint_names as $name) {
        $breakpoint_group += [$name => $name];
    }
    return $breakpoint_group;
}

protected function submitForm($form, &$form_state) {
    $state = $form_state->getTriggeringElement();
    $breakpoint= $state['#value'];
    $array = array();

    $breakpoint = \Drupal::service('breakpoint.manager')->getBreakpointsByGroup($breakpoint);
    $breakpoints_name = array_keys($breakpoint);

    foreach ($breakpoints_name as $breakpoint_definition) {
        $mediaQuery = $breakpoint[$breakpoint_definition]->getMediaQuery();
        array_push($array,$mediaQuery);
    }      

    $name_field = $form_state->get('num_names');
    $form_state->set('num_names',count($array));

    $form_state->setRebuild();

}

public function loadImageFields(array $form, FormStateInterface $form_state) {
    return $form['images_fieldset'];
 }

}

Alfred Armstrong avatar
cn flag
I'm not sure without testing but I wonder if the way you are creating the wrapper element with its ID is correct. Most of the working examples I can find use '#prefix' and '#suffix' to add a wrapper div.
Burly Uranus avatar
lb flag
@AlfredArmstrong I edited the code to put what I got so far! The widget does what it needs to do (generate fields) but the values don't get saved!
Alfred Armstrong avatar
cn flag
I can't see anything in your element code that gets the current value and sets the form up accordingly. (Typically you need $items[$delta]->value). You'll want the form to match how it would be if someone had selected that value and it had updated by ajax. For the value to be stored, by default it needs to be under $element['value'] so you might want to change your field item keys to be $element['value']['breakpoints'] and so on.
Burly Uranus avatar
lb flag
Thanks. The selected value of the dropdown gets saved but I still do not know how to add the fields depending on the dropdown. I edited my changes if you happen to know how to do it. The fields can't be added via AJAX but I don't know how to add them in another way. I tried adding them inside the formElement function but it doesn't work the same way as a normal drupal form.
Alfred Armstrong avatar
cn flag
You should check the value passed in when you build the form element and create corresponding form fields based on the value.
Burly Uranus avatar
lb flag
Thanks, I already got it figured out. I'll post the solution later.
Score:0
kz flag

The point is, you can't modify Drupal's internal form structure in an Ajax callback. So you'll need a submit handler, either for the field or for the complete widget, do the form modifications there and add a $form_state->setRebuild().

Ajax callbacks basically modify the HTML in the browser, that's all they do. They are called after the validation and submission has run.

Burly Uranus avatar
lb flag
Thanks for your answer! A submit handler? I'll try to do that. If you can point me out where to look it up I'd be grateful, but I'll search it anyways.
Burly Uranus avatar
lb flag
If you have time you can check my changes. Thanks anyways.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.