How do I validate form elements in a modal without closing? 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' =&gt; 'link',
  '#title' =&gt; t('Add NewEntity'),
  '#url' =&gt; Url::fromRoute(
    'my_module.add_entity',
    array(
      'node' =&gt; is_numeric($node) ? $node : NULL,
    )
  ),
  '#attributes' =&gt; [
    'class' =&gt; ['button', 'use-ajax'],
    'data-dialog-type' =&gt; 'modal',
  ],
  '#attached' =&gt; [
    'library' =&gt; ['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?
This is a excellent article which you can read for "fixing" your issue:
http://www.mediacurrent.com/blog/loading-and-rendering-modal-forms-drupal-8

Keep in mind this lines:

First, we wrap our form with a div

public function buildForm(array $form, FormStateInterface $form_state, $options = NULL) {
  // It is important this id
  $form['#prefix'] = '&lt;div id="modal_example_form"&gt;';
  $form['#suffix'] = '&lt;/div&gt;';

  // The status messages that will contain any form errors.
  $form['status_messages'] = [
    '#type' =&gt; 'status_messages',
    '#weight' =&gt; -10,
  ];

  // more code

  $form['actions']['send'] = [
      '#type' =&gt; 'submit',
      '#value' =&gt; $this-&gt;t('Submit modal form'),
      '#attributes' =&gt; [
        'class' =&gt; [
          'use-ajax',
        ],
      ],
      // Function to call when the users click on submit button.
      '#ajax' =&gt; [
        'callback' =&gt; [$this, 'submitModalFormAjax'],
        'event' =&gt; 'click',
      ],
    ];

    // more code
}


Second, we show the errors:

/**
 * AJAX callback handler that displays any errors or a success message.
 */
public function submitModalFormAjax(array $form, FormStateInterface $form_state) {
  $response = new AjaxResponse();

  // If there are any form errors, re-display the form.
  if ($form_state-&gt;hasAnyErrors()) {
    // Remember the previous id ? Here it is
    $response-&gt;addCommand(new ReplaceCommand('#modal_example_form', $form));
  }
  else {
    $response-&gt;addCommand(new OpenModalDialogCommand("Success!", 'The modal form has been submitted.', ['width' =&gt; 800]));
  }

  return $response;
}