Marius Ilie
Marius Ilie

Reputation: 3323

AJAX form validate and submit

I created a form in Drupal 7 and want to use AJAX. I added this to the submit button array:

"#ajax" => array(
  "callback" => "my_callback",
  "wrapper" => "details-container",
  "effect" => "fade"
)

This works but the whole validation function is ignored. How can I validate the form before my_callback() is called? And how can I display the status or error messages on a AJAX form?

Upvotes: 13

Views: 39263

Answers (6)

Brent Hartmann
Brent Hartmann

Reputation: 91

None of this works for me. The form submit's ajax function still just calls the callback function directly, bypassing validation, submit, and also making it so the button cannot be clicked multiple times. The validation messages do NOT get displayed. I literally copied and pasted Joshua Stewardson code and it did not work.

The fact that this usage case is so hard and undocumented is very upsetting. To me, this seems like the most basic of requests for an AJAX form API. Okay, done venting my frustration, onto the solution.

Here's what I ended up doing to get this to work. It feels wrong. It will also break if there are multiple instances of the form on a single page, but it was the best I could do. If anyone can shed light on this, PLEASE do!

Basically, you have to replace the whole form with itself inside your callback, and manually prepend any set messages to the form object. Do this by declaring the wrapper to be the id of your form (it will break if there are multiple instances of your form on a single page, because the ID will be updated).

function productsearchbar_savesearch_form($form, &$form_state) {

  $form["wrapper"] = array("#markup" => "<div class='inline-messages'></div>");

  $form["name"] = array(
    "#type" => "textfield", 
    "#required" => true,
    "#title" => "Name"
  );
    
  $form["submit"] = array(
    "#type" => "submit", 
    "#value" => "Send", 
    "#ajax" => array(
      "callback" => "productsearchbar_savesearch_form_callback", 
      "wrapper" => "productsearchbar-savesearch-form", 
      "effect" => "fade"
    )
  );

  return $form;
}

function productsearchbar_savesearch_form_callback($form, &$form_state) {
  $messages = theme('status_messages');

  if($messages){
    $form["wrapper"] = array("#markup" => "<div class='inline-messages'>$messages</div>");
  }
  return $form;
}

function productsearchbar_savesearch_form_validate($form, &$form_state) {
  if ($form_state['values']['name'] == '') {
   form_set_error('', t('Name field is empty!'));
  }
}

function productsearchbar_savesearch_form_submit($form, &$form_state) {
  drupal_set_message(t('Your form has been saved.'));
}

Upvotes: 9

Joshua Stewardson
Joshua Stewardson

Reputation: 874

This question and answer helped guide me to the right solution, but it isn't exactly crystal clear. Let's make it so.

Things to be very aware of:

  1. Validation error messages will be placed inside whatever is specified as the #ajax['wrapper']
  2. Pay close attention to where the Drupal Forms API documentation of the ajax wrapper says that "the entire element with this ID is replaced, not just the contents of the element."
  3. Because that element is replaced, you had better provide it again. That is why Marius Ilie's answer works - not because of the array and #markup, but rather because he is including the div with the wrapper id set.

Here is code that was working for me based off what Marius put in the comment above:

function dr_search_test_form($form, &$fstate) {
  $form["wrapper"] = array("#markup" => "<div id='test-ajax'></div>");

  $form["name"] = array(
    "#type" => "textfield",
    "#required" => true,
    "#title" => "Name"
  );

  $form["submit"] = array(
    "#type" => "submit",
    "#value" => t("Send"),
    "#ajax" => array(
      "callback" => "dr_search_test_form_callback",
      "wrapper" => "test-ajax",
      "effect" => "fade",
    ),
  );
  return $form;
}

function dr_search_test_form_callback($form, &$fstate) {
  return "<div id='test-ajax'>Wrapper Div</div>";
}

function dr_search_test_form_validate($form, &$fstate) {
  form_set_error("name", "Some error to display.");
}

Upvotes: 20

CLL
CLL

Reputation: 1342

I searched many hours for a way to do this properly. Unfortunately, most of these solutions still rely on Drupal's server side validation to determine whether the ajax results or the client side error messages should be placed in the wrapper. Relying on the server result is slower than client side validation, which should be virtually instantaneous. Also, replacing the form...with a form...with the error messages is a bit too messy for my preference.

Use jquery validation's methods to trigger an ajax event with a javascript trigger:

// Prevent form submission when there are client-side errors, trigger ajax event when it is valid
(function ($) {
    Drupal.behaviors.submitForm = { 
        attach: function (context) {
            var $form = $("form#validated_form", context);
            var $submitButton = $('input[type="submit"]', $form);

            $form
                .once('submitForm')
                .off('submit')
                .on('submit', function(e){

                    // Prevent normal form submission
                    e.preventDefault();
                    var $form = $(this);

                    // The trigger value should match what you have in your $form['submit'] array's ajax array
                    //if the form is valid, trigger the ajax event
                    if($form.valid()) {
                        $submitButton.trigger('submit_form');
                    }
            });

        }
    };
})(jQuery);

Reference the javascript trigger as your ajax event that is listened for:

$form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
    '#ajax' => array(
      'event' => 'submit_form',
      'callback' => 'callback_function_for_when_form_is_valid',
      'wrapper' => 'validated_form'
    )
);

Now, the validation is triggered as soon as the submit button is clicked, and the server side validation only happens once it's valid!

Upvotes: 0

bmunslow
bmunslow

Reputation: 370

I found an excellent solution to this problem.

Credit goes to this guy's blog:

http://saw.tl/validate-form-ajax-submit-callback

The solution he proposes is the following:

// when creating or altering the form..
{
  $form['#prefix'] = '<div id="formwrapper">';
  $form['#suffix'] = '</div>';
  // the submit button
  $form['save']['#ajax'] = array(
    'callback' => 'mymodule_form_ajax_submit',
    'wrapper' => 'formwrapper',
    'method' => 'replace',
    'effect' => 'fade',
  );
 // ...
}

function mymodule_from_ajax_submit($form, &$form_state) {
  // validate the form
  drupal_validate_form('mymodule_form_id', $form, $form_state);
  // if there are errors, return the form to display the error messages
  if (form_get_errors()) {
    $form_state['rebuild'] = TRUE;
    return $form;
  }
  // process the form
  mymodule_form_id_submit($form, $form_state);
  $output = array(
    '#markup' => 'Form submitted.'
  );
  // return the confirmation message
  return $output;
}

Upvotes: 10

chathura
chathura

Reputation: 17

//you can use jquery form validation

jQuery('#button-id').mousedown(function() {
  var textval = $("#get-text-val").val();
  if (textval == '') {
    $("#apend-error-msg").append('<div id="add-text-error" class="add-text-error">error msg</div>');
   return false;
  }
  else {
    return true;
  }
  return false;
 });

Upvotes: -1

Marius Ilie
Marius Ilie

Reputation: 3323

Ok, I figure it out. Apparently you should return an array on your ajax callback function, not just a text message...

Something like this:

return array("#markup" => "<div id='wrapper'></div>");

Upvotes: 7

Related Questions