1

I am updating an array saved in a users meta field using an ajax function.

The values added to the array are taken from the data-attributes within the tags which also act at the trigger to make the ajax call.

Whilst the function works 95% of the time, it can be a bit hit and miss whether the values save or not. I suspect this is because a user can fire these ajax calls too quickly and not give enough time for the original function call to save and update the meta field.

What would be the best method to ensure the ajax triggered function of updating the meta field value has been completed before allowing the function to run again?

Hope this makes sense - needless to say, please let me know if you need any more info.

Thanks in advance!!

Sample HTML

<div id="rjb_slots" class="slots">
  <h5>Mon, 24th Aug 2020</h5>
  <div class="slot">
    <span class="time">10:30</span>
    <a class="book" data-timestamp="1598265000" href="#"></a>
  </div>
  <div class="slot">
    <span class="time">11:00</span>
    <a class="booked" data-timestamp="1598266800" href="#"></a>
  </div>
  <div class="slot">
    <span class="time">11:30</span>
    <a class="booked" data-timestamp="1598268600" href="#"></a>
  </div>
  <div class="slot">
    <span class="time">12:00</span>
    <a class="book" data-timestamp="1598270400" href="#"></a>
  </div>
  <div class="slot">
    <span class="time">12:30</span>
    <a class="booked" data-timestamp="1598272200" href="#"></a>
  </div>
  <div class="slot">
    <span class="time">13:00</span>
    <a class="book" data-timestamp="1598274000" href="#"></a>
  </div>
  <div class="slot">
    <span class="time">19:30</span>
    <a class="book" data-timestamp="1598297400" href="#"></a>
  </div>
</div>

Ajax .js

$('.slot').on('click', 'a.book', function(e) {
  e.preventDefault();

  var user   = $('#rjb_day').attr( 'data-user' );
  var stamp  = $(this).attr( 'data-timestamp' );
  

  // console.log(bookCap);
  
  $(this).removeClass('book').addClass('booked');

  $.ajax({
    type: 'POST',
    url: ajax_object.ajaxurl,
    data: {
        action: 'rjb_make_diary_slots',
        user: user,
        stamp: stamp
    },
    success: function(data) {
      // This outputs the result of the ajax request
      console.log(data);
    },
    error: function(errorThrown){
        console.log(errorThrown);
    }
  });
    
});

Function that updates the user metafield

add_action( 'wp_ajax_rjb_make_diary_slots', 'rjb_make_diary_slots' );

function rjb_make_diary_slots() {

  $user   = $_POST['user'];

  $stamp  = array(
              array( 
                'rjb_cal_day'           => strtotime('today', $_POST['stamp']),
                'rjb_cal_when'          => $_POST['stamp'],
                'rjb_cal_position_id'   => '',
                'rjb_cal_candidate_id'  => ''
              )
            );

  $calendar   = get_user_meta( $user, 'rjb_cal', true);
  $stamps     = !empty($calendar) ? $calendar : array();
  $new_stamp  = array_merge($stamps, $stamp);

  usort($new_stamp, function($a, $b) {
    return $a['rjb_cal_when'] <=> $b['rjb_cal_when'];
  });

  update_user_meta( $user, 'rjb_cal', $new_stamp);

  $log = print_r($stamp);

  wp_die($log);

}

Example of a value stored in the rjb_cal user meta field

array (
  [0] => array (
    [rjb_cal_day] => 1598227200
    [rjb_cal_when] => 1598266800
    [rjb_cal_position_id] => 
    [rjb_cal_candidate_id] => 
  )
  [1] => array (
    [rjb_cal_day] => 1598227200
    [rjb_cal_when] => 1598268600
    [rjb_cal_position_id] => 
    [rjb_cal_candidate_id] => 
  )
  [2] => array (
    [rjb_cal_day] => 1598227200
    [rjb_cal_when] => 1598272200
    [rjb_cal_position_id] => 
    [rjb_cal_candidate_id] => 
  )
)

2 Answers 2

2

I usually use CSS classes to control AJAX requests affecting a specific element. This way you can prevent unwanted AJAX requests on that element, while other AJAX requests may still be triggered, being bound to other elements.

$('.slot').on('click', 'a.book', function(e) {
    e.preventDefault();

    // Check if doing ajax
    if($(this).hasClass("doing-ajax")) return false;
    $(this).addClass("doing-ajax");

    var user   = $('#rjb_day').attr( 'data-user' );
    var stamp  = $(this).attr( 'data-timestamp' );


    // console.log(bookCap);

    $(this).removeClass('book').addClass('booked');

    $.ajax({
        context: $(this),
        type: 'POST',
        url: ajax_object.ajaxurl,
        data: {
            action: 'rjb_make_diary_slots',
            user: user,
            stamp: stamp
        },
        success: function(data) {
           // This outputs the result of the ajax request
           console.log(data);

           // Remove "doing-ajax" class
           $(this).removeClass("doing-ajax");
        },
        error: function(errorThrown){
            console.log(errorThrown);

            // Remove "doing-ajax" class
           $(this).removeClass("doing-ajax");
        }
    });

});

Don't forget to add the context property to your AJAX's config object, as i did in the above example.

1
  • Thanks Dude - this has got me going!! Only slight change I had to make was adding the doing-ajax class to the parent element since the sibling links could also be clicked e.g.: $('#cvl_slots').addClass('doing-ajax'); I also added a .doing-ajax spinner with css for a bit of nice UX Aug 25, 2020 at 12:38
2

You can use two approach to prevent the user to click on your elem before complete the ajax fully. 1.Display loader and block that element so that no one can excute it until the ajax complete. 2.use a global var isAjaxProcess = false; now in your code make a condition block If(isAjaxProcess === false) { isAjaxProcess = true: Your ajax code goese here. And make isAjaxProcess to false in your ajax success block. }

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.