9

I have an Ajax multistep form and I want to redirect user to specific route when some conditions are met (specifically if the user is inactive for more than N seconds and the form is submitted after this limit).

I wanted to do the redirection in the validation handler but no luck. So I tried with a submit handler but still no luck. I'm quite buffled.

I tried to return a redirect response, I tried to set redirect response on the form state, I tried to set redirect on the form state, I have tried to disable form state rebuild, I have tried to copy the goto action logic and use an event dispatcher, I tried to unset the \Drupal\Core\Form\FormBuilderInterface::AJAX_FORM_REQUEST from request but still no luck.

I could maybe return the redirect in an Ajax callback as redirect command but I want to make this work with disabled JS so invoking window.location would not be enough.

4
  • You probably tried all things possible. But did you try to cover both cases separately? When js is enabled return a redirect command from the ajax callback. When js is disabled set a redirect url in submit and also rebuild to false.
    – 4uk4
    Commented Aug 11, 2017 at 6:31
  • @4k4 no, I did not, on purpose. I don't want to cover all possible situations. I want to have single logic in place.
    – user21641
    Commented Aug 11, 2017 at 7:29
  • It might be you have to, AJAX and POST requests are handled very differently. But sure there is some way to refactor the code to have the logic in one place and only execute the redirect in both ways.
    – 4uk4
    Commented Aug 11, 2017 at 7:47
  • Looking at \Drupal\Core\Form\FormBuilder::retrieveForm it seems the best approach would be to move this into buildForm() and return the redirect response instead of form array but all i got was, again, just the html of the desired page in response but no redirect. It looks like I will have to implement two solutions like I didn't want to.
    – user21641
    Commented Aug 11, 2017 at 7:51

2 Answers 2

15

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;
1
  • Superb...working good Commented Aug 1, 2019 at 10:18
4

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()
        )
      );
  }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.