bootstrap4 tagsinput, delete tag: how to check with ajax and then cancel

946 Views Asked by At

I am using tagsinput with bootstrap4. I want the user to click on deleting a tag (the 'x' button), then ajax the tag-delete request to the server which checks if user is allowed to do it. Server responds yes or no (and deletes the tag in DB if yes). And tagsinput should be notified to delete the tag from the input element and its internal state (on client side).

From what I understand, I add the beforeItemRemove event to the tagsinput-element and use the event.cancel=true; to cancel deletion of tag. Like this:

$(inputel).on('beforeItemRemove', function(event) {
  ajaxreq(..., onSuccess(r){}, onError(e,m){ event.cancel = true; });
});

But as you would have guessed the event.cancel is set too late because the event is long processed and exits before the ajax request is completed!

What I ended doing is this:

event.cancel = true; // we cancel the deletion by default
$(inputel).on('beforeItemRemove', function(event) {
  ajaxreq(..., onSuccess(r){
   // ajax succeeded and server allowed to delete, let's also delete from inputel
   $(inputel).tagsinput('remove', event.item, {preventPost: true});
}, onError(e,m){});
});
// the event is returning much earlier than ajax completing
// but with event.cancel=true it does nothing until we call the 'remove'

Is this the right solution?

EDIT: relevant documentation here: https://bootstrap-tagsinput.github.io/bootstrap-tagsinput/examples/ (search e.g. for beforeItemRemove)

EDIT2: I have just confirmed that this works in the case where the user is not allowed to delete a tag and the server via ajax refuses to delete it. And indeed nothing gets deleted in the input element. However, in the case where the user can and does delete the tag, the event is fired repeatedly (despite the preventPost: true which I only speculate that it removes without firing remove-event) and bombs the db.

1

There are 1 best solutions below

0
On

Cloning and patching the JS file bootstrap-tagsinput.js from https://bootstrap-tagsinput.github.io/bootstrap-tagsinput/examples/ was my last resort and it did work. So this in not ideal at all. Plus, there are lots of bootstrap "tagsinput" JS files out there which makes this a very bad solution.

The edits were in 3 places.

1) at registering the functions add and remove so that they accept a 3rd parameter, their signature shows they take 3 params in (the last one is the options as an object with keys like preventPost) but the registration below does not pass the 3rd arg! (mind you arg1 is function name like "add" or "remove"):

  /**
   * Register JQuery plugin
   */


   /* bliako modifies: add arg4 at the end, after arg3: */
  $.fn.tagsinput = function(arg1, arg2, arg3, arg4) {
    var results = [];

    this.each(function() {
...
      } else if(tagsinput[arg1] !== undefined) {
          // Invoke function on existing tags input
/* bliako modifies: */
var retVal = tagsinput[arg1](arg2, arg3, arg4);
/* bliako modifies: and comment out the following - which may introduce side-effects, the problem is that I can't see how these functions get an "options" 3rd arg without arg4 (arg1 is the function name, e.g."add") 
So this hopefully passes an options={preventPost: true} back to the add() and remove() functions.
*/
/*            if(tagsinput[arg1].length === 3 && arg3 !== undefined){
               var retVal = tagsinput[arg1](arg2, null, arg3);
            }else{
               var retVal = tagsinput[arg1](arg2);
            }
*/

2) modify the "add" function to NOT trigger events when options arg contains preventPost: true

      // if length greater than limit
      if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
        return;

      // raise beforeItemAdd arg
/* bliako modifies: don't trigger the &*Q&* event if preventPost == true */
if( (options == null) || ! options.hasOwnProperty('preventPost') || ! options.preventPost ){
      var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false, options: options});
      self.$element.trigger(beforeItemAddEvent);
      if (beforeItemAddEvent.cancel)
        return;
} else { console.log("tags/remove() : blocked beforeItemRemoveEvent"); }

3) The same modification as above but for the "remove" function:

        if (typeof item === "object")
          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  self.optio$
        else
          item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) ==  item; } );

        item = item[item.length-1];
      }

      if (item) {
/* bliako modifies: don't trigger the @*&*Q& event if preventPost == true */
if( (options==null) || ! options.hasOwnProperty('preventPost') || ! options.preventPost ){
        var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false, options: options $
        self.$element.trigger(beforeItemRemoveEvent);
        if (beforeItemRemoveEvent.cancel)
          return;
} else { console.log("tags/remove() : blocked beforeItemRemoveEvent"); }

The idea is simple and better to follow the idea to 1) allow add and remove functions to take a 3rd parameter as options so that you can pass add(item, dontpushval, {preventPost: true}); 2) modify said functions to actually read the options parameter (as their signatures already show, misleadingly, that they accept an options parameter) and whenever the preventPost exists and is set to true, to block triggering the beforeItemAdd and beforeItemRemove

This just shows how broken the "web" is and how billions worth of businesses excelling out there are founded on rotten, clay legs.

bw, bliako

p.s. I am astonished on the lack of any SO troll arriving instantly to declare this question irrelevant, off-topic, vague, already-solved or one of their "computer-says-no" excuse of the day. Perhaps they are all busy in a "community" meeting to make SO "greater".