Is your feature request related to a problem? Please describe.
It would be fantastic if the library could support asynchronous calls before continuing to the next step. For instance, it could be useful to make an intermediate AJAX call before going further in the process of a registration (where this library is useful).
Describe the solution you'd like
When the user clicks on Next button, a loader pops up (during the async call) and if the call is successful, the user is redirected to the next step, otherwise it stays at the same step with the error displayed
It should be usable with the custom events wz.nav.forward and wz.btn.next to allow the developer to do whatever he likes during the call
It should be able to be activable or not at a step level (which step is async, which is not)
Validation should be made before making the asynchronous call (indeed: no need to make a call when the form isn't even validated at a step)
Describe alternatives you've considered
@AdrianVillamayor in fact, I was able to make this behavior with small changes to your code, and a bit with my logic in my code.
I'll comment all the changes I've made here, because I'm not sure it's good enough for a PR, I'm pretty sure you can come with something great 👍
First and foremost, pretty much everything takes place in the onClick method, but I had to split this method into two distincts method, with this specific piece of code apart:
This allows us to set a data attribute like data-async-step="true" on the .wizard-step element, in order to control which step will requires additional call before continuing
Next, in the code that follows (it comes just after the const declarations), it was important to remove the event dispatcher. Indeed, the event should not be triggered from there (it's way too early, even way before the form is validated).
if (is_btn) {
if ($_.hasClass($this, this.wz_prev)) {
step = step - 1;
wz.dispatchEvent(new Event("wz.btn.prev")); <========== REMOVE HERE
} else if ($_.hasClass($this, this.wz_next)) {
step = step + 1;
wz.dispatchEvent(new Event("wz.btn.next"))); <========== REMOVE HERE
}
}
Just after the form validation, I store inside another const a reference to the proceedStep method created previously
I've moved the event dispatchers (of both is_btn and is_nav checkers) just after this, because at this point, the form has been validated so we could consider that the user can safely moves forward.
if (is_btn) {
if ($_.hasClass($this, this.wz_prev)) {
wz.dispatchEvent(new Event("wz.btn.prev"));
} else if ($_.hasClass($this, this.wz_next)) {
wz.dispatchEvent(new CustomEvent("wz.btn.next", {"detail": {"proceedFn": proceedFn, "step": step, "isAsync": isAsync}}));
}
}
if (is_nav) {
if (step_action) {
wz.dispatchEvent(new CustomEvent("wz.nav.forward", {"detail": {"proceedFn": proceedFn, "step": step, "isAsync": isAsync}}));
} else if (step < this.getCurrentStep()) {
wz.dispatchEvent(new Event("wz.nav.backward"));
}
}
if (!isAsync || this.getCurrentStep() > step) proceedFn();
// END OF THE onClick METHOD
There, this is all where the magic happens: if the developer did NOT specify the async option on the data attribute (!isAsync), or if the user is not going to a previous step (this.getCurrentStep() > step), only in these conditions we can proceed to display the next step.
Otherwise, it will NOT proceed, and the developer would have to manually make the step progress when he caught the event on his code. This is doable because the proceed method's reference is passed as a parameter to the CustomEvent, as well as the step number and the isAsync information.
Now, what about a business case with an example of mine in my own code?
This is how I've made it work like a charm and got exactly what I've wanted (to have an intermediate call before proceeding):
On the step I need to do an intermediate AJAX call, let's say step number 4, I've added the data attribute data-async-step="true" to the .wizard-step element.
Then, I simply had to create the event listener as below. Piece of cake 🥇
...
let wz_class = ".wizard";
let $wz_doc = document.querySelector(wz_class);
$wz_doc.addEventListener("wz.btn.next", async function(e) {
const proceedFn = e.detail.proceedFn;
const currentStep = e.detail.step;
const isAsync = e.detail.isAsync;
if (isAsync) { // <======== First, only do something if it's an async call
displayLoading(); // <======== Here I've got a function to pop up a display, preventing the user to click anywhere
switch(currentStep) { // <====== This is how I can control what call to make depending on the step (if we have several async calls for different steps, for instance)
case 4: // <======== For me, I only need an async call for step number 4
// Do my AJAX calls here... lot of awaits lol
if (ajaxResponse.success) {
// Yeee, I can continue and take the user to the next step
proceedFn();
} else {
// Don't call the proceedFn, so that the user stays at this step (and is displayed with another function of mine)
displayError(ajaxResponse.error.message);
}
stopLoading(); // <============ Don't forget to hide the loading to the user so he can clicks back (another fn)
break;
}
}
});
...
Obviously, feel free to implement this feature with a complete different logic, I just wanted to give a bit of help here ;)
Is your feature request related to a problem? Please describe.
It would be fantastic if the library could support asynchronous calls before continuing to the next step. For instance, it could be useful to make an intermediate AJAX call before going further in the process of a registration (where this library is useful).
Describe the solution you'd like
wz.nav.forward
andwz.btn.next
to allow the developer to do whatever he likes during the callDescribe alternatives you've considered
@AdrianVillamayor in fact, I was able to make this behavior with small changes to your code, and a bit with my logic in my code. I'll comment all the changes I've made here, because I'm not sure it's good enough for a PR, I'm pretty sure you can come with something great 👍
onClick
method, but I had to split this method into two distincts method, with this specific piece of code apart:I've wrapped this piece of code in another method called
proceedStep(e, step)
(will use it later)onClick
method, I've added the two following const at the beginning (with other consts):This allows us to set a data attribute like
data-async-step="true"
on the .wizard-step element, in order to control which step will requires additional call before continuingproceedStep
method created previouslyI've moved the event dispatchers (of both
is_btn
andis_nav
checkers) just after this, because at this point, the form has been validated so we could consider that the user can safely moves forward.There, this is all where the magic happens: if the developer did NOT specify the async option on the data attribute (
!isAsync
), or if the user is not going to a previous step (this.getCurrentStep() > step
), only in these conditions we can proceed to display the next step.Otherwise, it will NOT proceed, and the developer would have to manually make the step progress when he caught the event on his code. This is doable because the proceed method's reference is passed as a parameter to the
CustomEvent
, as well as thestep
number and theisAsync
information.Now, what about a business case with an example of mine in my own code?
This is how I've made it work like a charm and got exactly what I've wanted (to have an intermediate call before proceeding):
On the step I need to do an intermediate AJAX call, let's say step number 4, I've added the data attribute
data-async-step="true"
to the.wizard-step
element.Then, I simply had to create the event listener as below. Piece of cake 🥇
Obviously, feel free to implement this feature with a complete different logic, I just wanted to give a bit of help here ;)