Problem/Motivation
I recently attempted to add a constraint on a paragraph field so that a user could not select the same dropdown value twice. The constraint works but highlights the whole paragraph item rather than the field the error is on. This is misleading and possibly confusing for the end user.
Here is the content setup:
Content Type
- Entity Reference field to paragraph
Paragraph
- text list
- Entity reference to content
Actual:

Expected:

I found that field error handling is passed up to the widget and that InlineParagraphsWidget and ParagraphsWidget handle errorElement differently. For my use case, I want to drill down to the actual field causing the error so I started using "Paragraphs EXPERIMENTAL". While testing, I came to the understanding that FormState::setError expects the #parents key of the element to contain the correct property path to the element. Quick tangent, what constraints consider property paths and formState considers $name are identical except property paths expect "." to separate the field names and $name expects "][".
property path: field_quote_detail_page_componen.1.subform.field_component_name_wrapper
name: field_quote_detail_page_componen][1][subform][field_component_name_wrapper
Here is where the problem exists, (WidgetBase::flagErrors()):
foreach ($delta_violations as $violation) {
// @todo: Pass $violation->arrayPropertyPath as property path.
$error_element = $this->errorElement($delta_element, $violation, $form, $form_state);
if ($error_element !== FALSE) {
$form_state->setError($error_element, $violation->getMessage());
}
}
$this->errorElement will call ParagraphsWidget::errorElement which in terns calls NestedArray::getValue($element, $error->arrayPropertyPath). The returned element, $error_element, will have a parent key as follows:
result = {array} [4]
0 = "field_quote_detail_page_componen"
1 = 1
2 = "subform"
3 = "field_component_name_wrapper"When FormState::setError is called it uses the above array to build at the $name arg for FormState::setErrorByName, field_quote_detail_page_componen][1][subform][field_component_name_wrapper. While we cannot assign an error to a wrapper element the error message will show but there is no error styling to alert the user where the error is.
Proposed resolution
Escalate the todo in WidgetBase::flagErrors():
// @todo: Pass $violation->arrayPropertyPath as property path.
Come up with workaround
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #5 | 3022513-dont-check-for-empty-on-a-property-accessed-with-magic-#5.patch | 1.63 KB | tcrawford |
| paragraph-validation-field.png | 29.8 KB | robpowell | |
| paragraph-validation-item.png | 31.05 KB | robpowell |
Comments
Comment #2
robpowellFor now, I would suggest extending one of the paragraphFieldWidget and overriding the errorElement with the following:
Comment #3
robpowellComment #4
tcrawford commentedParagraphsWidget::errorElement() attempts to get the sub element when the path is valid. It currently checks for empty($this->arrayPropertyPath) where arrayPropertyPath is got via magic on Drupal\Core\Field\InternalViolation, but InternalViolation does not implement __isset(). Therefore the code on errorElement never returns $sub_element, even if there is a valid sub element found by NestedArray::getValue(). We could open an issue in core for InternalViolation to implement __isset(), but likely that won't move forward as accessing arrayPropertyPath via magic is deprecated. Therefore, I would propose the following solution (patch to follow):
Comment #5
tcrawford commentedPatch attached.
Comment #6
tcrawford commentedComment #7
herved commentedHi @robpowell, @tcrawford,
It would be great if you could check out the patch in #3333974: Stop using $error->arrayPropertyPath which should fix both issues (this one + the deprecation of arrayPropertyPath), so we could close this as duplicate.
Thanks
Comment #8
berdirThe referenced issue is now committed. Could still use this to add test coverage.