2

I'm often using Drupal::messenger() to show status/warning messages before I submit the form. But those messages are usually counter-intuitive once the form has been submitted. How can I add a message to a form that is only rendered before the form has been submitted.

My typical code looks like this:

function buildForm() {
  /**
   * ...build form...
   */
  $showWarning = $this->doSomeChecks();

  /**
   * tried to use isSubmitted() to prevent the message after submitting, but does not work
   */
  if ($showWarning  && !$form_state->isSubmitted()) {
    $this->messenger()->addWarning('Some info you should know before submitting this form.');
  }
}

function submitForm(array &$form, FormStateInterface $form_state) {
  /**
   * ...do stuff...
   */
  $this->messenger()->addMessage('Successfully done something');

  /**
   * tried to use deleteByType() to remove message from buildForm after submitting, but does not work
   */
  $this->messenger()->deleteByType($this->messenger()::TYPE_WARNING);
}

I know I could use $form_state->setRedirect($customPage) in submitForm(), but I don't want the overhead of creating a new page just to hide a simple warning message.

Is there no simple way to show a message only once on the initial form load, but not after submit?

3 Answers 3

2

Try

if (empty($form_state->getUserInput())) {
  $this->messenger()->addWarning('Some info you should know before submitting this form.');  
}

See the discussions here: Avoid repetitive API calling in form rebuild while submitting

8
  • Sadly does not work. The buildForm is called multiple times during submit, in my case the first run was with a non-empty UserInput as expected, but then for whatever reason there was a second buildForm call with an empty UserInput.
    – Hudri
    Commented Jul 29, 2020 at 12:08
  • I've been taking a look at the linked question, I'm also never getting that isRebuilding() flag in my form. I'm currently trying to suppress a message on a very simple ContentEntityConfirmFormBase, this form tyüe doesn't seem to setRebuild at all
    – Hudri
    Commented Jul 29, 2020 at 12:16
  • It should work, I've used this in the past successfully. Are you redirecting to the same form after submit? Then you see a new form and a new message. Try for testing to redirect to a different URL in form submit $form_state->setRedirect('<front>');. Then check both your and my condition and you should see the difference.
    – 4uk4
    Commented Jul 29, 2020 at 12:46
  • Well, that is actually my problem :-) I explicitly do not want to use $form_state->setRedirect($whatEver), I want to stay on the same page/form, and just conditionally show one or the other message. Redirecting to <front> would be a bad shift in UX, creating a custom success page for a simple success message is an annoying overhead.
    – Hudri
    Commented Jul 29, 2020 at 12:52
  • I can only answer what to do instead of $form_state->isSubmitted(). The rest of the form logic is not in the question. But if you initiate a redirect to the same form without storing any of the submitted information Drupal can' tell what happened before.
    – 4uk4
    Commented Jul 29, 2020 at 12:57
0

This is my current version working for my simple usecase. Since buildForm() is triggered multiple times during a single submit request, the combined check empty($form_state->getUserInput()) && !$form_state->isRebuilding() allowed me to detect if the form was initially loaded/not submitted yet.

class MyConfirmForm extends ContentEntityConfirmFormBase {

  public function getQuestion() {
    return $this->t('Do you want to do stuff?');
  }

  function buildForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildForm($form, $form_state);

    $showWarning = $this->doSomeChecks();

    if ($showWarning && empty($form_state->getUserInput()) && !$form_state->isRebuilding()) {
      $this->messenger()->addWarning('Some info you should know before submitting this form.');
    }

    return $form;
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);

    $result = $this->doStuff();

    if ($result) {
      $this->messenger()->addMessage('Successfully done something');
      $form_state->setRebuild(TRUE);
    }
  }
}

I'm leaving the question open though, because @4k4 indicated in comments above that using $form_state->setRebuild() inside submitForm() is not a good practice.

3
  • $form_state->setRebuild() is always inside of submit. The question is for what reason. The downside, if you do it without needing a rebuild to refine the form, you generate unnecessary form cache items. You use them only to store it as information between the form submit and the next request, which you could do with other means more efficiently like a session value or query parameter.
    – 4uk4
    Commented Jul 29, 2020 at 13:56
  • Not in my experience, I manually had to add $form_state->setRebuild(TRUE); in my submitForm(), otherwise the check for $form_state->isRebuilding() was always false.
    – Hudri
    Commented Jul 29, 2020 at 14:10
  • That's the point, you add it only to check later $form_state->isRebuilding(). If this is the only reason then it is not good practice.
    – 4uk4
    Commented Jul 29, 2020 at 14:32
0

You can try

$request = \Drupal::request()->server;
$http_referer = $request->get('HTTP_REFERER');
$showWarning = TRUE;
if ($http_referer) {
  $host = $request->get('HTTP_HOST');
  $request_scheme = $request->get('REQUEST_SCHEME');
  $current_url = str_replace($request_scheme . '://' . $host, '', $http_referer);
  $url_object = \Drupal::service('path.validator')->getUrlIfValid($current_url);
  $route_name = $url_object->getRouteName();
  if ($route_name === 'custom.router') {
    $showWarning = FALSE;
  }
}

if ($showWarning &&  $this->doSomeChecks()) {
  this->messenger()->addWarning('Some info you should know before submitting this form.');
}

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.