Support

Account

Home Forums General Issues Frontend form – post via ajax?

Solving

Frontend form – post via ajax?

  • Hi!

    I already have an ACF form that end users can use. But when I click submit button, page is reloaded. Is it possible to submit form via ajax somehow?

    Any tips and ideas would be appreciated:)

  • Hi,

    Same here, I was wondering if that is possible and how if it is. Or any hints for a workaround to make that trick work.

  • OK this is just a thought as I am only just starting to tackle this myself but it seems that if you just add a bit of javascript to prevent the submit button, you can get all the form fields into an object yourself and then post that through ajax the same way you would any other form.

    A brief theoretical code example:

    jQuery(":submit").click(function(event){
        event.preventDefault();
        var form_data = {"action" : "acf/validate_save_post"};
        jQuery("form#post :input").each(function(){
          form_data[jQuery(this).attr("name")] = jQuery(this).val()
        })
         jQuery.post(ajaxurl, form_data) // you will need to get the ajax url for you site
         .done(function(data){
           //you get back something like {"result":1,"message":"Validation successful","errors":0}
         })
      })

    That would validate the form. After that, you need to submit it. To do that, add actions somewhere in your functions.php to point ajax requests to the acf head function:

    add_action( 'wp_ajax_save_my_data', 'acf_form_head' );
    add_action( 'wp_ajax_nopriv_save_my_data', 'acf_form_head' );

    That will point ajax requests to the acf form head function.

    then you can call that using the same js

    form_data.action = "save_my_data";
            jQuery.post(ajaxurl, form_data)
            .done(function(save_data){
              console.log(save_data)
            })
    

    This can go in the return function of the validation ajax call, with an if statement to check the validation came back ok.

    Only problem I have found so far is it returns a url it wants to go to which throws a js error. Working on that now…

  • Just to update as I realised I never finished this – to get past the problem of it returning the url, I added an extra hook for after ACF_Head had saved which wrote out a new form with the new post_id which I can replace the existing one with. That way I can resave the form again without creating another new entry.

    So my final save code looks like this:

    function my_pre_save_post($post_id) {
    	if( $post_id == 'new_data' ) {
    	    $post = array(
    	        'post_status'  => 'publish' ,
    	        'post_type'  => 'saved_data',
    	    );
    	    $post_id = wp_insert_post($post); 
    	}
    	return $post_id;
    }
    
    function my_acf_save_post( $post_id )
    {
    	acf_form(array(
    		'post_id'		=> $post_id,
    		'field_groups' => array(180)
    	));
    	exit();
        
    }
    
    add_filter( 'acf/pre_save_post', 'my_pre_save_post' );
    add_action( 'wp_ajax_save_app_data', 'acf_form_head' );
    add_action( 'wp_ajax_nopriv_save_app_data', 'acf_form_head' );
    add_action( 'acf/save_post', 'my_acf_save_post', 20 );

    If anyone needs any help with this let me know but I also realise this might not be the most efficient way to achieve an AJAX save…

  • Hey rsrc1147,

    Thanks for this, it’s a nice start.

    Wondering if you can help me out with implementing something similar to this.

    Cheers

  • @rsrc1147 thanks a lot for the code example, it helped me greatly.

    i only have two additional questions:

    1) this code works perfectly with text inputs, selects etc, but it doesn’t seem to be able to save radio buttons and checkboxes (so basically elements which use hidden elements), is there a way around it?

    2) i am not sure if i understand the validation part correctly. in the code example, required inputs are checked whether they’ve been filled, but for instance email fields are not being validated for their type (so user can just fill anything).

    thanks in advance for any kind of information about these issues.

  • Three years after the original question, now there is a clean way to achieve this:

    
    // call this function on document ready or when your ajax page is loaded
    function renderPage() {
      // initialize the acf script
      acf.do_action('ready', $('body'));
    
      // will be used to check if a form submit is for validation or for saving
      let isValidating = false;
    
      acf.add_action('validation_begin', () => {
        isValidating = true;
      });
    
      acf.add_action('submit', ($form) => {
        isValidating = false;
      });
    
      $('.acf-form').on('submit', (e) => {
        let $form = $(e.target);
        e.preventDefault();
        // if we are not validating, save the form data with our custom code.
        if( !isValidating ) {
          // lock the form
          acf.validation.toggle( $form, 'lock' );
          $.ajax({
            url: window.location.href,
            method: 'post',
            data: $form.serialize(),
            success: () => {
              // unlock the form
              acf.validation.toggle( $form, 'unlock' );
            }
          });
        }
      });
    }
    

    Hope it’s self explanatory.

  • Hi @rasso,
    I’ve been wrapping my head around this for hours, and it’s THAT simple… Thank you!
    However I don’t fully understand some parts of code you used:

    acf.validation.toggle( $form, 'lock' );
    (...)
    acf.validation.toggle( $form, 'unlock' );

    Where this comes from? I couldn’t find it in docs

  • @danielrutkowski

    all of this stuff is un-documented. I got this directly from acf-input.js. It locks and un-locks the form, to prevent duplicate submissions.

    EDIT

    …just realized that there actually is documentation on this.

    Still, skimming through acf-input.js is still a good idea.

  • Hi @rasso & @danielrutkowski,

    I have used your code to do some tests and I finally modify it to limit the number of Ajax call :

    
    	acf.do_action('ready', $('body'));
    
        $('.acf-form').on('submit', function(e){
            e.preventDefault();
        });
    
    	acf.add_action('submit', function($form){
    		$.ajax({
    			url: window.location.href,
    			method: 'post',
    			data: $form.serialize(),
    			success: function(data) {
    				acf.validation.toggle($form, 'unlock');
                    alert(data);
    			}
    		});
    	});
    

    In the actual code the AJAX success has no response data. If you want to get the “update message” set in the form declaration you can use this hook :

    
    add_action('acf/submit_form', 'my_acf_submit_form', 10, 2);
    function my_acf_submit_form( $form, $post_id ) {
    
        if(is_admin())
            return;
    
        //Test if it's a AJAX request.
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
            echo $form['updated_message'];
            die();
        }
    
    }

    I hope this code can help you! ๐Ÿ˜‰

  • @valpe and @rasso and all thank you for providing those code snippets, it’s really great starting point for those who doesn’t have much clue.

    However I’m afraid the basic uploader isn’t covered or is it?

    Using the above methods I’m able to submit a new custom post, but only wp native uploader attachments are actually being saved. I recall that the native uploader actually “does upload” during its dialog, while the basic one doesn’t.

    How would you suggest to include those attachments by the ajax call?
    Or maybe it can be hooked in via ‘acf/pre_save_post’ using wp_handle_upload and updating them fields with attachment IDs accordingly? I noticed file names are present in the $form e.g. field_5a29532806a20%5D=url%3DC%253A%255Cfakepath%255Ca8WRYY1_700b.jpg%26size%3D327854%26type%3Dimage%252Fjpeg%26width%3D600%26height%3D2944

    Any help is greatly appreciated. Needless to say everything works just fine w/o ajax. I’m on 5.6.5 pro.

  • This code is great, but I also need to handle some custom validation and errors. So my questions are:

    1) How do you handle and display any validation errors?

    2) How can I add in custom validation?

    I was doing this with a add_filter('acf/validate_value/) call, but with the AJAX submit on the form when this fails it seems to just halt the ajax (keeps spinning forever).

    I’m guessing the solution has something to do with moving my code to the acf.add_filter('validation_complete') JS filter, but any guidance would be appreciated. Thanks!

  • Hi @phylaxis,

    I have a custom validation message save in custom field settings.
    It’s maybe not the best solution but it’s works fine for me!

    add_action('acf/render_field_settings', '_pit_add_field_setting_for_error_message');
    function _pit_add_field_setting_for_error_message( $field ) {
    
        $args = array(
            'type' => 'text',
            'label' => 'Erreur formulaire',
            'name' => 'error_form',
            'instructions'	=> 'Erreur affichรฉ a l\'utilisateur lors du remplissage du formulaire',
            'required' => 0,
            'default_value' => "Veuillez remplir ce champ"
        );
        acf_render_field_setting($field, $args, false);
    
    }
    
    add_filter('acf/validate_value', '_pit_acf_form_error_message', 10, 4);
    function _pit_acf_form_error_message( $valid, $value, $field, $input ){
    
        if(!$valid)
            $valid = $field['error_form'];
    
        return $valid;
    
    }

    The first hook set the field and the second one print the error if the field is not valid. It’s works for me in AJAX form. I hope this code will help you. If you have a best way to to it I’m more than interrested!

    I have try to set the field only when is required but it was a fail for me.. It’s works only when the field is save one time.

    Sorry @pooledge I have try with the basic uploader and it’s doesn’t works for me too.

    Thanks! ๐Ÿ˜‰

  • Hi @pooledge,
    I have find a solution for you. The problem is the serialization of the file. If you want to bypass this you have to use ‘FormData’. This code works for me with the basic uploader :

    acf.add_action('submit', function($form){
    
                var formdata = new FormData($form[0]);
    
                $.ajax({
                    url: window.location.href,
                    method: 'post',
                    data: formdata,
                    cache: false,
                    processData: false,
                    contentType: false,
                    success: function(data) {
                        acf.validation.toggle($form, 'unlock');
                        $form.prepend('<div class="acf-error-message -success">' + data + '</div>');
                        $('html,body').animate({
                            scrollTop: $form.offset().top - 150
                        }, 500);
                        $('.acf-form button[type="submit"]').prop('disabled', false);
    
                    }
                });
            });

    I hope this code will help you! ๐Ÿ˜‰ Thanks!

  • @valpe You are a star. This works just fine.

    Un grand merci ร  toi et bon journรฉe!

  • Hey everyone,

    There’s actually a way to use one and single ajax request during a acf_form() front-end posting.

    You can take advantage of the acf/validate_save_post hook which is fired at the beginning of the acf_form() ajax validation request.

    You just have to trigger acf_get_validation_errors() with a wp_send_json_success return in order to keep the inherent ACF Form validation (required field, minimum/maximum etc…). Then, you must use new acf_form_front() to process the form in your ajax request, just like ACF Form does when it reloads the page after validation.

    Here is an exemple of the hook usage:

    
    add_action('acf/validate_save_post', 'hwk_ajax_acf_form_hook');
    function hwk_ajax_acf_form_hook(){
        if(!wp_doing_ajax() || !isset($_POST['_acf_post_id']) || !acf_verify_nonce('acf_form'))
            return;
            
        // Native ACF Form validation (required, minimum/maximum etc...)
        if($errors = acf_get_validation_errors())
            wp_send_json_success(array(
                'valid' 	=> 0,
                'errors' 	=> $errors,
            ));
        
        // acf_form() arguments are stocked in $_POST['_acf_form']
        if(!$form = $_POST['_acf_form'])
            return;
        
        // Decoding the form arguments via acf_decrypt().
        $form = json_decode(acf_decrypt($form), true);
            
        // Creating a 'proxy form' for the Legacy ACF Form fields saving
        // Setting 'return' to null to avoid built-in redirection
        $proxy = $form;
        $proxy['return'] = '';
    
        // Using native ACF Form submission method
        acf()->form_front = new acf_form_front();
        acf()->form_front->submit_form($proxy);
    
        wp_send_json_success(array(
            'valid' => 1,
            'data' 	=> 'Success!',
        ));
    }
    

    The javascript part use the acf.add_filter('validation_complete') filter. Remember to add preventDefault() on form submission to avoid the legacy acf_form() page reload.

    
    $('.acf-form.acf-form-ajax').on('submit', function(e){
        e.preventDefault();
    });
    

    If you’re interested about the whole process, I’ve written a full tutorial, including the creation of a Login, Register, Lost password & My Account forms with ACF Form, 100% in Ajax: https://hwk.fr/blog/acf-creer-un-formulaire-de-connexion-inscription-en-ajax-grace-acf-form

    It’s written in french, but the code is english-friendly ๐Ÿ˜‰

  • Hi @hwk , I testet your code and it works great! The only thing now still missing is a way to hook into the validation ajax request, so that I could include image upload capabilities, as described by @valpe . Do you know how to do that? You seem like a pro ๐Ÿ˜‰

    …in the meantime, I used this code (still two ajax requests, but much cleaner than my original solution):

    
    acf.add_filter('validation_complete', ( json, $form ) => {
      // if errors?
      if( json.errors ) {
        return json;
      }
    
      var formdata = new FormData($form[0]);
    
      $.ajax({
        url: window.location.href,
        method: 'post',
        data: formdata,
        cache: false,
        processData: false,
        contentType: false,
        success: (data) => {
          acf.validation.toggle($form, 'unlock');
          //... show success message ...
        }
      });
    
      return json;
          
    });
    
  • Hello!

    Sorry for the late answer! In fact, if you use the argument 'uploader' => 'basic' in your acf_form call to use the native browser upload button, then it won’t be properly uploaded by the form.

    For you information, 'uploader' => 'wp' (the WP upload popup) works, but I don’t want to show it on front forms. So I decided to re-work my code in order to make it fully compatible with the native browser uploader.

    Here is the Javascript: https://gist.github.com/hawkidoki/6302e2ecddcc6e3a6150fabc4e8cae6c

    I decided to rewrite the function acf.validation.fetch to use formdata instead of classic ACF Form ajax calls. Note that I remove the data.action = 'acf/validate_save_post' argument, because I’ll add my own Ajax action for each one of my ACF Forms.

    Here is the PHP: https://gist.github.com/hawkidoki/e49fbc385794c1f1c5d8db1504723cea

    Using the action acf/input/form_data, I’ll add my own <input type="hidden" name="action" value="hwk_ajax_acf_register_ajax_submit" />. Now the data.action is set, the Javascript call will understand what Ajax action I want to reach.

    Then, I define my own PHP Ajax fonctions:

    
    add_action('wp_ajax_hwk_ajax_acf_register_ajax_submit', 'hwk_ajax_acf_register_ajax_submit');
    add_action('wp_ajax_nopriv_hwk_ajax_acf_register_ajax_submit', 'hwk_ajax_acf_register_ajax_submit');
    
    • Checking the nonce with acf_verify_nonce(‘acf_form’)
    • Validating data with acf_validate_save_post() (which call the action acf/validate_save_post)
    • And finally, process my data…

    I hope everything is clear. I also updated my article, so the login/register/lost password & account forms use the same method.

    Let me know if you need some more explanations ๐Ÿ™‚

  • Hey Guys,

    Is there away to get the newly created post ID in the ajax success call or even to send it to php?

  • @ibes Actually, yes! There is a submit action now. Very handy. Also, I included a fix for empty file inputs breaking Safari, the new IE… ;(

    
    acf.addAction('submit', ( $form ) => {
    
      // Fix for Safari Webkit โ€“ empty file inputs kill the browser
      // https://stackoverflow.com/a/49827426/586823
      let $fileInputs = $('input[type="file"]:not([disabled])', $form)
      $fileInputs.each(function(i, input) {
        if( input.files.length > 0 ) {
          return;
        }
        $(input).prop('disabled', true);
      })
    
      var formData = new FormData( $form[0] );
    
      // Re-enable empty file $fileInputs
      $fileInputs.prop('disabled', false);
    
      acf.lockForm( $form );
    
      $.ajax({
        url: window.location.href,
        method: 'post',
        data: formData,
        cache: false,
        processData: false,
        contentType: false
      }).done(response => {
        console.log(response);
        acf.unlockForm( $form );
      });
      
    
    });
    
  • @rasso
    Thanks for your update!

    I don’t manage to make it work.

    I am sad that there is no official documentation about that.
    Seems like everything for it would be there.

    I guess I just go for non-ajax and take care that the state of “it is saved, but now it is not fresh anymore” is clearer to see …

  • For me .addAction('submit' doesnt trigger.

  • I was having similar issue with with the latest update. The form would not submit when I did something like $('yourform').trigger('submit');. I changed my code to instead use the doAction and that worked. ex acf.doAction('submit', $('yourform'));

  • I think I found a better way. So in 5.7.13 on line 13386 of /assets/js/acf-input.js there is some new logic that checks if the submit event has already bee prevented. This is why things stopped working. In the examples above we are setting preventDefault() for the submit event.

    Looking further in acf-input.js onSubmit we can see there are a few lines to run the form validation. That validation also eventually runs acf.doAction('submit', this.$el); when validation is successful.

    I could not find a way to get around the new logic in acf-input.js onSubmit so I still keep the preventDefault() on the submit event. But then run the validation method to submit the form. Something like this:

    `
    $(document).on(‘submit’, ‘.acf-form’, function(e) {
    e.preventDefault();
    });

    $(‘.save’).on(‘click’, function(event) {
    if ($(‘.acf-form’).length > 0) {
    var valid = acf.validateForm({
    form: $(‘.acf-form’),
    event: acf.get(‘originalEvent’)
    });
    }
    });
    `

Viewing 25 posts - 1 through 25 (of 30 total)

The topic ‘Frontend form – post via ajax?’ is closed to new replies.