Drupal - How to do a hard redirect when in Ajaxified form?

In my case (D8), I just had to implement the RedirectCommand in my AjaxSubmit :

Code of the submit button :

$form['submit_' . $this->uniqueIdentifier] = [
  '#type' => 'submit',
  '#value' => $this->t('Submit'),
  '#attributes' => [
    'class' => ['btn', 'btn-primary']
  ],
  '#ajax' => [
    'callback' => [$this, 'submitModalFormAjax'],
    'event' => 'click',
  ],
];

Code of the submit callback:

public function submitModalFormAjax(array $form, FormStateInterface $form_state) {
  $response = new AjaxResponse();

  if ($form_state->hasAnyErrors()) {
    // Do validation stuff here
    // ex: $response->addCommand(new ReplaceCommand... on error fields
  }

  else {
    // Do submit stuff here

    $url = Url::fromRoute('page_route');
    $command = new RedirectCommand($url->toString());
    $response->addCommand($command);
  }

  return $response;
}

Do not forget to declare :

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Url;

After quite some experimenting I have came to this working solution.

The condition has to be in the beginning of the submit handler:

  if ($condition) {
    $form_state->setRedirect('foo.bar')->disableRedirect(FALSE)->setRebuild(FALSE);
    // Return here so the code that activates form rebuilding for ajax or any data processing logic is not invoked.
    return;
  }

And the ajax callback has to be formatted like this:

public static function ajaxRebuildForm(array $form, FormStateInterface $form_state) {
    /** @var \Drupal\Core\Form\FormSubmitterInterface $submitted */
    $submitter = \Drupal::service('form_submitter');
    /** @var \Symfony\Component\HttpFoundation\RedirectResponse $redirect */
    $redirect = $submitter->redirectForm($form_state);
    if ($redirect) {
      return (new AjaxResponse())->addCommand(new RedirectCommand($redirect->getTargetUrl()));
    }

    return $form;
  }

This is working for both js-enabled and js-disabled environment. If JS is enabled the ajax callback will detect the set redirection and return ajax response with redirect command instead of render array(which is internally turned into ajax response as well). If the js is disabled the redirect is handled as redirect response when form is submitted.


Edit: I have found that this is not 100% working solution. I have a route controller that either returns the form or perofrms redirect. Once I'm in the ajaxified form and the controller does the redirect(ie. some conditions were un/met) I'm stuck on the form and cannot do anything since the ajax callback is not triggered so I am basically doing requests to page that returns redirects and no form logic is invoked anymore. Which means I have to implement additional logic, again, into the controller, not just the form, since the form API won' have a chance to get invoked and process the form in the first place.

I had to implement this in my controller:

  if ($request->request->get(AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER)) {
    return (new AjaxResponse())
      ->addCommand(
        new RedirectCommand(
          $this->redirect(
            $this->currentRouteMatch->getRouteName()
          )->getTargetUrl()
        )
      );
  }

Tags:

Forms

Ajax

8