The fields below are part of the node/add node/edit forms of my custom node type.

enter image description here

The paragraph field_para_city contain one field_city. (I later need to add other fields in the paragraph).

The user can add multiple instances from field_para_city.

field_city has an allowed_values_function defined in a YML file, which returns all possible cities from all countries.

I created a class MyNodeTypeForm extends NodeForm, with a public function buildForm(). This function adds AJAX to field_country so that when it changes (i.e. the user chooses a country) it (re-)populates all the existing instances of field_city (inside the field_para_city instances) with corresponding list of cities (e.g., London and Cardiff for the UK).

In MY_MODULE.module, I added the following so that the above code is executed on the node/add and node/edit forms.

function MY_MODULE_entity_type_alter(array &$entity_types) {
  $handlers = $entity_types['node']->get('handlers');
  $handlers['form']['default']  = 'Drupal\MY_MODULE\Form\MyNodeTypeForm';
  $handlers['form']['edit']     = 'Drupal\MY_MODULE\Form\MyNodeTypeForm';
  $entity_types['node']->set('handlers', $handlers);

All works fine, except when the user add or remove instances of field_para_city ("Add city" or "Remove"), then all instances of the field_para_city are refreshed and field_city instances inside them are reset to the allowed values (all cities), ignoring the state of field_country.

The user would then need to re-select from field_country to get the correct cities again.

I found that although adding/removing a field_para_city triggered the above mentioned buildForm() function, the $form_state->getValue('field_country') was then empty, unlike when buildForm() is triggered through the field_country AJAX.

I tried to capture the state of field_country in the field_city's allowed_values_function, so that the allowed options are dynamically and directly set based on the user choice of country, but $form_state was not recognised there.

I know this is an incorrect approach!

What is the correct better approach to implement this simple use case?

  • A question, have you tried or are you using drupal.org/project/conditional_fields ? – Ismail Cherri Feb 20 at 12:38
  • I thought about it. It allows changing field value based on another field's value, but I actually need something beyond that: the dependent field options should be programmatically populated taking into account also some existing contents and settings in the website. – Orion Feb 20 at 13:41
  • I believe buildForm() runs everytime the AJAX handler is called. Therefore, many people opt to have the complete logic in buildForm() and then the ajax handler simply returns the $form back without touching it (maybe setting $form_state->setRebuild(FALSE);) . This thread may have relevant info on ajax forms: drupal.stackexchange.com/questions/236942/… – cchoe1 Feb 21 at 14:00
  • I actually have a different problem. The AJAX re-builds the form after being triggered by changes in field_country here. But what I need is a mechanism to rebuild the form (or at least the dependent field) when a new dependent field is being added. So the trigger here is the creation of the dependent field, at which I need to access the value of the controlling field. – Orion Feb 21 at 14:37
  • May I invite the experts to help answering this question? – Orion Feb 23 at 10:57

I managed to make it work using a different approach; instead of overriding the NodeFrom, I used hook_field_widget_WIDGET_TYPE_form_alter and edited the subform array there.
/** * Implements hook_field_widget_WIDGET_TYPE_form_alter(). */ function MY_MODULE_field_widget_entity_reference_paragraphs_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) { //Logic to exclude other Paragraph types if ($element['#paragraph_type'] !== 'YOUR_PARAGRAPH_TYPE'){ //e.g. cities return; } $element['subform']['field_places']['#states'] = [ 'visible' => [ [ //It worked for me with the ID of the select, I think you can use the name also '#edit-field-cities-0-subform-field-city' => array('value' => 'Cairo'), ] ] ]; } When I add another paragraph, the ajax is fired and the selection is still preserved.
I haven't tried with other Ajax fields present though.

  • Thanks for this. However, I'm not sure how it exactly solves the problem. In my question, the problem is when I add another paragraph that the allowed values (options) in the list in the existing paragraph are reset but the selection is actually preserved. – Orion Feb 28 at 23:57
  • By the way, did you mean field_country instead of field_places? If so, I wasn't able to get the AJAX of the field_county to be fired.. – Orion Feb 28 at 23:58
  • Oh sorry, it seems I misunderstood your question. I tried applying a different approach with the same hook but it didn't work for me neither. The fields kept being deselected with addition of new field item. field_places is totally irrelevant to your issue. Sorry again – Ismail Cherri Mar 1 at 9:34
  • No problem! I really appreciate your answer though! – Orion Mar 1 at 10:54

Your Answer

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

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