I'm using Drupal 8 and I have defined a custom entity type 'my_entity_type', with form handlers defined in the @ContentEntityType annotation:
* @ContentEntityType(
...
* handlers = {
* "form" = {
* "add" = "Drupal\my_module\Form\MyEntityForm",
* "edit" = "Drupal\my_module\Form\MyEntityForm",
* "delete" = "Drupal\my_module\Form\MyEntityDeleteForm",
...
where:
- MyEntityForm extends ContentEntityForm
- MyEntityDeleteForm extends ContentEntityConfirmFormBase
These are routed in my_module.routing.yml leveraging the _entity_form element:
_entity_form: my_entity_type.add
I am rendering the form from another form as a modal:
$form['add_my_entity_button'] = [
'#type' => 'link',
'#title' => t('Add NewEntity'),
'#url' => Url::fromRoute(
'my_module.add_entity',
array(
'node' => is_numeric($node) ? $node : NULL,
)
),
'#attributes' => [
'class' => ['button', 'use-ajax'],
'data-dialog-type' => 'modal',
],
'#attached' => [
'library' => ['core/drupal.dialog.ajax'],
],
];
All works as imagined. The '#required' type validator appears in the form in the modal.
Goal
To add custom validation to this form's elements ('live' validation that does not close the modal). I recognize that this type of problem isn't novel, and I've done it often in non-modal forms. The methods I would usually leverage are:
- '#element_validate' form element callback
- validateForm() type form callback
Both appear to require submission of the form, close the modal, and render the errors in a flat form display. I then combed through Core for ideas and embarked on a remarkably twisted yak-shaving journey:
Adding validation via BaseFieldDefinition::addConstraint in the entity baseFieldDefinitions() : This still submitted the form and led to a flat (non modal) form with errors correctly thrown. Once I solve this problem, I will likely leave this in to serve as a gatekeeper at the storage level.
Extending the 'number' FieldType via Plugin, extending the widget, type or storage : This seemed overly complex for something as simple as adding a validator. I bailed on this before getting too far, since I considered:
Injecting a generic '#ajax' 'replace' callback to the element inside buildForm() in MyEntityForm : I could NOT get ajax inserted in this manner to fire here.
Injecting a generic '#ajax' 'replace' callback to the element in hook_form_alter : I could NOT get ajax callbacks inserted in this manner to fire either.
Questions
What have I missed? I feel this is simple. Is there something I've missed in Form that defines simple ajax validation?
Is there a standard way for validating form elements inside modals without closing the modal?
Can AJAX 'replace' callbacks be attached to forms defined and routed via the @ContentEntityType annotation? Is there a restriction here in the render pipeline?