Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Sweet Alert: Create a Promise!

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

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:

... 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:

... 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:

... 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():

... 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:

... 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:

... 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:

... 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!

7
Login or Register to join the conversation
Default user avatar
Default user avatar Brian Morris | posted 5 years ago

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

2 Reply
Default user avatar
Default user avatar Radoje Albijanic | posted 5 years ago

Hey guys,
Great Tutorial! For future people coding along with this video don't use latest sweetalert version (js and css, currently `7.11.0`) because it won't work with this code, you cannot `catch` alert dismiss with `catch()`. Use version `6.1.0`. Correct me if I'm wrong :)

Reply

Hey Radoje Albijanic

Look's like you are right, I just did a quick review over the new changes in the latest version and there are many breaking changes
Thanks for letting us know, I'll talk to the team to see what can we do about it.

Cheers!

Reply
Michael-K Avatar
Michael-K Avatar Michael-K | posted 5 years ago

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

Reply

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!

Reply
Michael-K Avatar

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

Reply

Hey Michael,

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

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial uses an older version of Symfony... but since it's a JavaScript tutorial, the concepts are still ? valid!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.1.*", // v3.1.10
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.1
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.2
        "symfony/swiftmailer-bundle": "^2.3", // v2.4.0
        "symfony/monolog-bundle": "^2.8", // 2.12.0
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "~2.0@dev", // dev-master
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.1
        "symfony/phpunit-bridge": "^3.0" // v3.1.6
    }
}
userVoice