0

I have a wizard/form using JQuery Steps to add a customer to our internal website and I need to validate an email/telephone/name server side to check for preexisting customers. PHP returns the number of matches. If they don't exist, JQuery Steps has an onStepChanging event which needs a return value of true to allow it to proceed to the next 'step' (it naturally returns false).

I have used 'async:false' in my Ajax request, which works, but I get a warning about it being deprecated and in this instance, I need it to run synchronously. I want to do it properly and follow best practice especially if 'async:false' does get deprecated. Apparently, it will be deprecated in order to improve user experience by not freezing processes.

If I remove 'async:false', then when I call my checkExisting(), I will always get 0 returned because it won't update my 'matches' variable since it's running asynchronously, and I understand this but I need to return the number of matches.

I've tried altering my ajax and returning ajax1, then doing

checkExisting(..).done(function(){... return true})

but this doesn't seem to work either and returns [object Object]. It returns me out of the .done() but doesn't pass this return true/false to the onStepChanging:function(){...}

Normally, I would do something along the lines of

validateThing().done(function() {
    // if invalid, add a red border around the input
    // add validation message
});

I have read several answers and they seem to suggest restructuring your code to improve the user experience. Some suggested to return the ajax and using .done() or .then(). However, I'm bound to this onStepChanging event in jQuery Steps and I need to return true/false.

// this is what currently works with async:false

function checkExisting(email) {

    let matches = 0;
    let ajax1 = $.ajax({
        url: "/includes/pages/organisations/ajax.php",
        type: "GET",
        async: false,
        data: {action: 'checkExisting', value: email},
        success: function(response) {
            const responseData = JSON.parse(response);
            matches =  responseData.matches;
        }
    });

    return matches;
}



// JQuery Steps 
$('#addNewCustomer').steps({

onInit: function() {},
onStepChanging: function(e,currentIndex, newIndex) {

    let stepStatus = false;

    if(checkExisting(customer.email) === 0) {
        // customer exists
        stepStatus = true;
    } else {
        // add validation error message
        // add validation styling
    }

    return stepStatus;
}
...


});

I need to be able to call checkExisting() and have it return a number. I can then use this to return true/false to JQuery Steps, and I can also use it to report the number of matching customers in the validation message ("3 customers exist with this number").

Apologies if I'm being incredibly dense and I'm missing something obvious!

Thanks for your time.

ilkerkaran
  • 4,214
  • 3
  • 27
  • 42
ross9998
  • 1
  • 1
  • You cannot use ordinary `return` to get values back from asynchronous operations. You have to use callbacks or Promises (which are basically another way of using callbacks). In modern JavaScript you can handle Promises with `async` functions. – Pointy Apr 03 '19 at 13:52
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – ponury-kostek Apr 03 '19 at 13:55
  • That library advertises the ability to do asynchronous stuff, but I can't tell how it works because their documentation pages seem to be broken. – Pointy Apr 03 '19 at 13:55
  • @ponury-kostek I have read that post and I keep going back to it in the hope that something clicks. If you see the **'Side note: Promise gotchas'** part, that's where I'm having my issue. I'd have to do `checkExisting().done(function(){ // somehow return number of matches });` But that's what I can't seem to get right. – ross9998 Apr 03 '19 at 14:08

1 Answers1

0

For anyone else who has the same issue, I came up with a solution/hack to get asynchronous requests working. I had tried returning a promise and resolving it but JQuery Steps needs a true/false value - not a promise.

  1. Declare a global variable. e.g

let allowStepsProgress = false

  1. Inside your onStepsChanging event, add an if statement
onStepChanging: function(e, currentIndex, newIndex) {

    if(allowStepsProgress) {
        // reset it back to false
        allowStepsProgress = true;
        // proceed to next 'step'
        return true;
    }

    /* 
        Do whatever you want here inc ajax requests, grabbing form values etc.
        If you want to progress depending on your ajax response then you must return false
    */

}
  1. Within the onStepChanging function, you can then do any async request:
    $.ajax(...).then(function(response) {
        if(...) {
            // we're happy and want to allow user to proceed to next step
            allowStepProgress = true;
            // Click the step, and this will proceed immediately because of Step 2.
            $('#step_next').click();
        } else {
           // do error styling
        }
    });

Et voila!

To summarise:

  • User fills first tab of your JQuery Steps and clicks next
  • onStepChanging is fired
  • The page does not proceed (because we've returned false) but ajax request has been fired
  • Response from ajax comes back, we set allowStepsProgress = true and simulate click 'next'
  • onStepChanging is fired
  • The 'if' statement is hit and returns true before the rest of the onStepChanging code runs
  • User proceeds to second tab of your JQuery Steps

Note

The downside to this is that JQuery Steps will cause your current step tab to turn red (because we've returned false as a default) then, once we allow the user to proceed, it will turn to the success colour and then proceed onto the next step.

It's not that noticeable for me because my ajax request is resolved quickly, however you may want to add a class or something to prevent it turning red whilst waiting for your response. If your request takes a while, then you'd need some sort of loader gif so that the user knows something is going on in the background.

I hope that helps

ross9998
  • 1
  • 1