@@ -43,6 +43,9 @@ public function getInfo() {
4343 [$ class , 'processPluginSelect ' ],
4444 [$ class , 'processAjaxForm ' ],
4545 ],
46+ '#after_build ' => [
47+ [$ class , 'clearValues ' ],
48+ ],
4649 '#element_validate ' => [
4750 [$ class , 'validateElementSubmit ' ],
4851 [$ class , 'validatePlugin ' ],
@@ -72,18 +75,17 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
7275 // Prefix and suffix used for Ajax replacement.
7376 $ element ['#prefix ' ] = '<div id=" ' . $ ajax_wrapper_id . '"> ' ;
7477 $ element ['#suffix ' ] = '</div> ' ;
75-
7678 $ element ['#tree ' ] = TRUE ;
79+
7780 $ element ['target_plugin_id ' ] = [
7881 '#type ' => $ element ['#plugin_element_type ' ],
7982 '#title ' => $ element ['#title ' ],
8083 '#multiple ' => FALSE ,
8184 '#ajax ' => [
82- 'callback ' => [get_called_class (), 'pluginFormAjax ' ],
85+ 'callback ' => [get_called_class (), 'ajaxRefresh ' ],
8386 'wrapper ' => $ ajax_wrapper_id ,
8487 ],
8588 '#default_value ' => $ target_plugin_id ,
86- '#ajax_array_parents ' => $ element ['#array_parents ' ],
8789 '#required ' => $ element ['#required ' ],
8890 ];
8991 // Add a "_none" option if the element is not required.
@@ -102,8 +104,8 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
102104 continue ;
103105 }
104106
105- // Group categorized plugins.
106- if (isset ($ definition ['category ' ])) {
107+ // Group categorized plugins, and if using a select element .
108+ if (isset ($ definition ['category ' ]) && $ element [ ' #plugin_element_type ' ] == ' select ' ) {
107109 $ element ['target_plugin_id ' ]['#options ' ][(string ) $ definition ['category ' ]][$ definition ['id ' ]] = $ definition ['label ' ];
108110 }
109111 else {
@@ -122,10 +124,13 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
122124 $ element ['target_plugin_configuration ' ] = [
123125 '#type ' => 'container ' ,
124126 ];
125- if ($ target_plugin_id != '_none ' ) {
127+ if (! empty ( $ target_plugin_id ) && $ target_plugin_id != '_none ' ) {
126128 /** @var \Drupal\Core\Executable\ExecutableInterface $plugin */
127129 $ plugin = $ plugin_manager ->createInstance ($ target_plugin_id , $ values ['target_plugin_configuration ' ]);
128130 if ($ plugin instanceof PluginFormInterface) {
131+ $ element ['target_plugin_configuration ' ] = [
132+ '#tree ' => TRUE ,
133+ ];
129134 $ element ['target_plugin_configuration ' ] = $ plugin ->buildConfigurationForm ($ element ['target_plugin_configuration ' ], $ form_state );
130135 }
131136 }
@@ -136,15 +141,14 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
136141 /**
137142 * Ajax callback.
138143 */
139- public static function pluginFormAjax (&$ form , FormStateInterface $ form_state , Request $ request ) {
140- $ triggering_element = $ form_state ->getTriggeringElement ();
141- while (!isset ($ triggering_element ['#ajax_array_parents ' ])) {
142- array_pop ($ triggering_element ['#array_parents ' ]);
143- $ triggering_element = NestedArray::getValue ($ form , $ triggering_element ['#array_parents ' ]);
144- }
145- $ element = NestedArray::getValue ($ form , $ triggering_element ['#ajax_array_parents ' ]);
144+ public static function ajaxRefresh (&$ form , FormStateInterface $ form_state , Request $ request ) {
145+ $ target_plugin_id_element = $ form_state ->getTriggeringElement ();
146146
147- return $ element ;
147+ // Radios are an extra parent deep compared to the select.
148+ $ slice_length = ($ target_plugin_id_element ['#type ' ] == 'radio ' ) ? -2 : -1 ;
149+
150+ $ plugin_select_element = NestedArray::getValue ($ form , array_slice ($ target_plugin_id_element ['#array_parents ' ], 0 , $ slice_length ));
151+ return $ plugin_select_element ;
148152 }
149153
150154 /**
@@ -180,8 +184,9 @@ public static function validatePlugin(array &$element, FormStateInterface $form_
180184 // If a plugin was selected, create an instance and pass the configuration
181185 // values to its configuration form validation method.
182186 if ($ target_plugin_id != '_none ' ) {
187+ /** @var \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager */
183188 $ plugin_manager = \Drupal::service ('plugin.manager. ' . $ element ['#plugin_type ' ]);
184- $ plugin = $ plugin_manager ->createInstance ($ target_plugin_id , $ values ['target_plugin_configuration ' ]);
189+ $ plugin = $ plugin_manager ->createInstance ($ target_plugin_id , $ element [ ' #default_value ' ] ['target_plugin_configuration ' ]);
185190 if ($ plugin instanceof PluginFormInterface) {
186191 $ plugin ->validateConfigurationForm ($ element ['target_plugin_configuration ' ], $ form_state );
187192 }
@@ -202,8 +207,9 @@ public static function submitPlugin(array &$element, FormStateInterface $form_st
202207 // If a plugin was selected, create an instance and pass the configuration
203208 // values to its configuration form submission method.
204209 if ($ target_plugin_id != '_none ' ) {
210+ /** @var \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager */
205211 $ plugin_manager = \Drupal::service ('plugin.manager. ' . $ element ['#plugin_type ' ]);
206- $ plugin = $ plugin_manager ->createInstance ($ target_plugin_id , $ values ['target_plugin_configuration ' ]);
212+ $ plugin = $ plugin_manager ->createInstance ($ target_plugin_id , $ element [ ' #default_value ' ] ['target_plugin_configuration ' ]);
207213 if ($ plugin instanceof PluginFormInterface) {
208214 /** @var \Drupal\Component\Plugin\ConfigurablePluginInterface $plugin */
209215 $ plugin ->submitConfigurationForm ($ element ['target_plugin_configuration ' ], $ form_state );
@@ -213,4 +219,28 @@ public static function submitPlugin(array &$element, FormStateInterface $form_st
213219 }
214220 }
215221
222+ /**
223+ * Clears the plugin-specific form values when the target plugin changes.
224+ *
225+ * Implemented as an #after_build callback because #after_build runs before
226+ * validation, allowing the values to be cleared early enough to prevent the
227+ * "Illegal choice" error.
228+ */
229+ public static function clearValues (array $ element , FormStateInterface $ form_state ) {
230+ $ triggering_element = $ form_state ->getTriggeringElement ();
231+ if (!$ triggering_element ) {
232+ return $ element ;
233+ }
234+
235+ $ triggering_element_name = end ($ triggering_element ['#parents ' ]);
236+ if ($ triggering_element_name == 'target_plugin_id ' ) {
237+ $ input = &$ form_state ->getUserInput ();
238+ $ parents = array_merge ($ element ['#parents ' ], ['target_plugin_configuration ' ]);
239+ NestedArray::setValue ($ input , $ parents , '' );
240+ $ element ['target_plugin_configuration ' ]['#value ' ] = '' ;
241+ }
242+
243+ return $ element ;
244+ }
245+
216246}
0 commit comments