Sweet Alert: Create a Promise!

And we also also know that both functions are passed a value, and what that value is depends on the library. Add an arg to .catch() and log it:

190 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 48
handleRepLogDelete: function (e) {
... lines 50 - 54
swal({
... lines 56 - 60
}).catch(function(arg) {
console.log('canceled', arg);
});
},
... lines 65 - 170
});
... lines 172 - 188
})(window, jQuery, Routing, swal);

Ok, refresh, hit delete and hit cancel. Oh, it's a string: "cancel". Try it again, but hit escape this time to close the alert. Now it's esc. Interesting! If you search for "Promise" on its docs, you'll find a spot called "Handling Dismissals". Ah, it basically says:

When an alert is dismissed by the user, the reject function is passed one of these strings, documenting the reason it was dismissed.

That's pretty cool. And more importantly, it was easy for us to understand.

Kung fu by Creating another Promise

Because we understand Promises, there's one other really cool thing we can do. Search for preConfirm. If you pass a preConfirm option, then after the user clicks "Ok", but before SweetAlert closes, it will call your function. You can do anything inside... but if what you want to do is asynchronous, like an AJAX call, then you need to return a Promise from this function. This will tell SweetAlert when your work is done so that it knows when it's ok to close the alert.

Let's try it! First, add a showLoaderOnConfirm option set to true:

198 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 48
handleRepLogDelete: function (e) {
... lines 50 - 54
swal({
title: 'Delete this log?',
text: 'What? Did you not actually lift this?',
showCancelButton: true,
showLoaderOnConfirm: true,
... lines 60 - 70
});
},
... lines 73 - 178
});
... lines 180 - 196
})(window, jQuery, Routing, swal);

That will show a little loading icon after the user clicks "OK". Next, add the preConfirm option set to a function. Inside, return a new Promise with the familiar resolve and reject arguments:

198 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 48
handleRepLogDelete: function (e) {
... lines 50 - 54
swal({
... lines 56 - 59
preConfirm: function() {
return new Promise(function(resolve, reject) {
... lines 62 - 64
});
}
... lines 67 - 70
});
},
... lines 73 - 178
});
... lines 180 - 196
})(window, jQuery, Routing, swal);

Just to fake it, let's pretend we need to do some work before we can actually delete the RepLog, and that work will take about a second. Use setTimeout() to fake this: pass that a function and set it to wait for one second. After the second, we'll call resolve():

198 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 48
handleRepLogDelete: function (e) {
... lines 50 - 54
swal({
... lines 56 - 59
preConfirm: function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 1000);
});
}
... lines 67 - 70
});
},
... lines 73 - 178
});
... lines 180 - 196
})(window, jQuery, Routing, swal);

Try it! Refresh and click delete. After I hit ok, you should see a loading icon for one second, before the alert finally closes. Do it! There it was! Viva promises!

More realistically, sometimes - instead of doing my work after the alert closes, I like to do my work, my AJAX call, inside of preConfirm. After all, SweetAlert shows the user a pretty fancy loading icon while they're waiting. Let's do that here - it's super easy!

Move the self._deleteRepLog() call up into the preConfirm function and return it. Then get rid of the .then() entirely:

193 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 48
handleRepLogDelete: function (e) {
... lines 50 - 54
swal({
... lines 56 - 59
preConfirm: function() {
return self._deleteRepLog($link);
}
}).catch(function(arg) {
// canceling is cool!
});
},
... lines 67 - 173
});
... lines 175 - 191
})(window, jQuery, Routing, swal);

This is totally legal, as long as the _deleteRepLog() function returns a Promise. In other words, as long as we return $.ajax(), SweetAlert will be happy:

193 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 67
_deleteRepLog: function($link) {
... lines 69 - 78
return $.ajax({
... lines 80 - 86
})
},
... lines 89 - 173
});
... lines 175 - 191
})(window, jQuery, Routing, swal);

We can still keep the catch here, because if you hit cancel, that will still reject the promise and call .catch(). Head back, refresh, and click delete. You should see the loading icon for just a moment, while our AJAX call finishes. Hit "Ok"! Beautiful!

Cleanup My Mistakes

Oh, and by the way, if you noticed that I was still using .done() in a few places, that was an accident! Let's change this to .then(), and do the same thing in loadRepLogs:

193 lines web/assets/js/RepLogApp.js
... lines 1 - 2
(function(window, $, Routing, swal) {
... lines 4 - 26
$.extend(window.RepLogApp.prototype, {
... lines 28 - 31
loadRepLogs: function() {
... line 33
$.ajax({
... line 35
}).then(function(data) {
... lines 37 - 39
})
},
... lines 42 - 67
_deleteRepLog: function($link) {
... lines 69 - 78
return $.ajax({
... lines 80 - 81
}).then(function() {
... lines 83 - 86
})
},
... lines 89 - 173
});
... lines 175 - 191
})(window, jQuery, Routing, swal);

Now we're using the true Promise functions, not the .done() function that only lives in jQuery.

Woh, we're done! I hope you guys thoroughly enjoyed this weird dive into some of the neglected parts of JavaScript! In the next tutorial in this series, we're going to talk about ES6, a new version of JavaScript, which has a lot of new features and new syntaxes that you probably haven't seen yet. But, they're critical to writing modern JavaScript.

All right guys, see you next time.

Leave a comment!

  • 2017-09-26 Victor Bocharsky

    Hey Michael,

    Good find! I haven't used it by myself, but looks really nice! Btw, thanks for sharing it with us ;)

    Cheers!

  • 2017-09-25 Michael

    Hey Victor,

    Thanks a lot for this detailed answer! It wasn't that easy for me.. but now I used a plugin called Bootstrap Notify (http://bootstrap-notify.rem...) for a really nice styled and animated message ;-)

    Cheers!
    Michael

  • 2017-09-25 Victor Bocharsky

    Hey Michael,

    Thanks! Hm, interesting question. Probably, the better way do not use Symfony flash messages at all. If the row was successfully deleted on server side - you can just return success AJAX response data with that message and read it with JS, i.e. show it in some bootstrap HTML wrapper to show it in nice styles - see Alerts section in TB docs: https://getbootstrap.com/do... . You can even write your custom method/helper for showing any messages in such styles. And probably, if you do not use translations, you don't even need to return this message from controller, just hardcode that message inside JS code, and when delete request was successful - just show it.

    Cheers!

  • 2017-09-23 Michael

    Hi
    Thanks a lot for this really great tutorial!!
    One question:
    Is there an easy way to show the flash message after the row is deleted? I know how I can set a flash message in the controller, but the message only appears when the page is reloaded.

    Best regards!
    Michael

  • 2017-02-22 Brian Morris

    Great course!!! Very much looking forward to the next one!!