@@ -155,6 +155,13 @@ protected function buildJsonFormsForm(
155155 */
156156 public function validateForm (array &$ form , FormStateInterface $ formState ): void {
157157 parent ::validateForm ($ form , $ formState );
158+ if ([] !== $ formState ->getErrors () && [] === $ this ->determineLimitValidationErrors ($ formState )) {
159+ // Even though the triggering element has #limit_validation_errors set to
160+ // [] form state might contain errors, e.g. if a no radio button of a
161+ // required radios element was selected. This might happen on
162+ // recalculation.
163+ $ formState ->clearErrors ();
164+ }
158165
159166 if ($ formState ->isSubmitted () || $ formState ->isValidationEnforced ()) {
160167 FormCallbackExecutor::executePreSchemaValidationCallbacks ($ formState );
@@ -240,4 +247,54 @@ protected function doGetSubmittedData(FormStateInterface $formState): array {
240247 return FieldNameUtil::toJsonData ($ data );
241248 }
242249
250+ /**
251+ * Copied from
252+ * \Drupal\Core\Form\FormValidator::determineLimitValidationErrors().
253+ *
254+ * Determines if validation errors should be limited.
255+ *
256+ * @param \Drupal\Core\Form\FormStateInterface $formState
257+ * The current state of the form.
258+ *
259+ * @return array<mixed>|null
260+ *
261+ * phpcs:disable Generic.Files.LineLength.TooLong
262+ */
263+ private function determineLimitValidationErrors (FormStateInterface &$ formState ): ?array {
264+ // While this element is being validated, it may be desired that some
265+ // calls to \Drupal\Core\Form\FormStateInterface::setErrorByName() be
266+ // suppressed and not result in a form error, so that a button that
267+ // implements low-risk functionality (such as "Previous" or "Add more") that
268+ // doesn't require all user input to be valid can still have its submit
269+ // handlers triggered. The triggering element's #limit_validation_errors
270+ // property contains the information for which errors are needed, and all
271+ // other errors are to be suppressed. The #limit_validation_errors property
272+ // is ignored if submit handlers will run, but the element doesn't have a
273+ // #submit property, because it's too large a security risk to have any
274+ // invalid user input when executing form-level submit handlers.
275+ $ triggering_element = $ formState ->getTriggeringElement ();
276+ if (isset ($ triggering_element ['#limit_validation_errors ' ]) && ($ triggering_element ['#limit_validation_errors ' ] !== FALSE ) && !($ formState ->isSubmitted () && !isset ($ triggering_element ['#submit ' ]))) {
277+ return $ triggering_element ['#limit_validation_errors ' ];
278+ }
279+ // If submit handlers won't run (due to the submission having been
280+ // triggered by an element whose #executes_submit_callback property isn't
281+ // TRUE), then it's safe to suppress all validation errors, and we do so
282+ // by default, which is particularly useful during an Ajax submission
283+ // triggered by a non-button. An element can override this default by
284+ // setting the #limit_validation_errors property. For button element
285+ // types, #limit_validation_errors defaults to FALSE, so that full
286+ // validation is their default behavior.
287+ elseif ($ triggering_element && !isset ($ triggering_element ['#limit_validation_errors ' ]) && !$ formState ->isSubmitted ()) {
288+ return [];
289+ }
290+ // As an extra security measure, explicitly turn off error suppression if
291+ // one of the above conditions wasn't met. Since this is also done at the
292+ // end of this function, doing it here is only to handle the rare edge
293+ // case where a validate handler invokes form processing of another form.
294+ else {
295+ return NULL ;
296+ }
297+ // phpcs:enable
298+ }
299+
243300}
0 commit comments