Use jQuery Deferred inside plugin to always call cleanup method
-
27-10-2019 - |
سؤال
I'm writing a jQuery UI plugin. Inside that plugin, when an action occurs, I'm invoking one of the plugin options as a callback. Once that callback is completed, I want to run some cleanup code.
To be more specific, my plugin uses jQuery UI draggable and droppable. On droppable drop, I invoke a function defined in the options called update. After update is called, which is an AJAX call, I want to perform some cleanup. I don't want the user of the plugin to be required to perform this cleanup call; I want the cleanup call to happen automatically after the update AJAX method is successful.
I thought using jQuery's Deferred made sense here. Here's some code for the plugin's drop implementation:
self.connectedLists = $(self.options.connectWith)
.not(self.list)
.droppable({
hoverClass: 'ui-selectablelist-active',
drop: function(e, ui) {
var sender = $(ui.draggable).closest('ul'),
deferred = self.options.update.call(self, e, {
sender: sender,
receiver: $(this),
items: selectedItems
});
deferred.then(function () {
self.removeSelectedItems();
});
}
});
And the code for the plugin implementer looks like this:
update: function (e, ui) {
var self = this;
return $.post(url,
{
// some data
})
.done(function (data) {
console.log('updated!');
});
}
I'm returning the AJAX call as a promise to the drop callback. Inside the drop callback, I want to perform the cleanup operation removeSelectedItems always, so I use the .then() function. It doesn't seem to be running.
Does this pattern sound like a good idea. Can anyone help me with this design? Why isn't my done function running inside the drop callback?
المحلول
Instead of using .then
, use .always
.
.then
is used for adding callbacks to a deferred object:
deferred.then(donecallbacks,failcallbacks)
Try:
self.connectedLists = $(self.options.connectWith)
.not(self.list)
.droppable({
hoverClass: 'ui-selectablelist-active',
drop: function(e, ui) {
var sender = $(ui.draggable).closest('ul'),
deferred = self.options.update.call(self, e, {
sender: sender,
receiver: $(this),
items: selectedItems
});
deferred.always(function () {
self.removeSelectedItems();
});
}
});
Update:
Since the developer will be specifying the update function, there's always a possibility of the developer not properly returning the deferred object to you. You should check for that and throw an exceiption in that case.
self.connectedLists = $(self.options.connectWith)
.not(self.list)
.droppable({
hoverClass: 'ui-selectablelist-active',
drop: function(e, ui) {
var sender = $(ui.draggable).closest('ul'),
deferred = self.options.update.call(self, e, {
sender: sender,
receiver: $(this),
items: selectedItems
});
if (deferred.always) {
deferred.always(function () {
self.removeSelectedItems();
});
}
else {
$.error("Update must return a deferred object.");
}
}
});