Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent duplicate with Bootstrap Tokenfield When using Jquery Ui Autocomplete

I am trying to implement Bootstrap Tokenfield with Jquery Ui autocomplete and so far i was able to do that except the fact that i am not able to prevent duplicates in the input field, so, unfortunately my user can choose the same value twice.

In my search i have found that Bootstrap Tokenfield has a way of preventing duplicate. However I do not know how to apply to my code because it looks to me that it goes with Twitter typeahead and not Jquery Ui.

How can i prevent duplicate with Bootstrap TokenField Using Jquery Ui autocomplete ?

This is my Bootstrap TokenField code based on jquery ui autocomplete

$('.tokenfield').tokenfield({
  autocomplete: {
    source: [
    {
        "id": "1",
        "value": "Ferdine Faithfull" 
    },
    {
        "id": "2",
        "value": "John Carta" 
    },
    {
        "id": "3",
        "value": "Mezane Smith" 
    }
    ],

    delay: 100
  },
  showAutocompleteOnFocus: true
});

And below is what i have found on Github to prevent duplicate though i think it is for Typeahead

$('#my-tokenfield').on('tokenfield:createtoken', function (event) {
    var existingTokens = $(this).tokenfield('getTokens');
    $.each(existingTokens, function(index, token) {
        if (token.value === event.attrs.value)
            event.preventDefault();
    });
});
like image 291
Ferdine Faithfull Avatar asked Feb 24 '15 06:02

Ferdine Faithfull


3 Answers

I think you've done it all, all you are left to do is to replace the class

So after the first code, instead of the second code write

$('.tokenfield').on('tokenfield:createtoken', function (event) {
    var existingTokens = $(this).tokenfield('getTokens');
    $.each(existingTokens, function(index, token) {
        if (token.value === event.attrs.value)
            event.preventDefault();
    });
});

The difference here is your class that has to be applied and it works both for Twitter Typeahead and Jquery Ui

like image 137
Man Of God Avatar answered Oct 26 '22 20:10

Man Of God


This prevents listing items that have already been added as tokens:

    $('.tokenfield').on('tokenfield:createdtoken tokenfield:removedtoken', function (event) {
    var field = $(this);
    var currentTokens = field.tokenfield('getTokens');
    var originalSource = field.data('bs.tokenfield').options.autocomplete.source;
    var newSource = originalSource.slice(); //clone original autocomplete source
    for (var i = newSource.length - 1; i >= 0; i--) {
      for (var j = currentTokens.length - 1; j >= 0; j--) {
        if (JSON.stringify(currentTokens[j].label) == JSON.stringify(newSource[i]) 
          || JSON.stringify(currentTokens[j]) == JSON.stringify(newSource[i]) ) {
          //remove the token from the newSource
          var index = newSource.indexOf(newSource[i]);
          if (index > -1) 
            newSource.splice(index, 1);
        };
      };
    };
    //update source
    field.data('bs.tokenfield').$input.autocomplete({source: newSource})
})

This function is called after token is created or deleted to update the list. It uses JSON.stringify() to compare objects, and does the comparison for string objects and for {value: "foo", label: "bar"} source objects.

like image 25
Javier Giovannini Avatar answered Oct 26 '22 20:10

Javier Giovannini


@Javier Your solution work good but sometimes it gets buggy and add twice the token! Have you got idea for this behaviour?

PS After seen the documentation i found the solution. Both event handling are needed. Because events are fired before and after creation/edit/remove of tokens.

So you need this to prevent the add (before create event)

$('#tokenfield').on('tokenfield:createtoken', function (event) {
    var existingTokens = $(this).tokenfield('getTokens');
    //check the capitalized version
    event.attrs.value =  capitalizeFirstLetter(event.attrs.value);
    $.each(existingTokens, function(index, token) {
        if (token.value === event.attrs.value) {
            event.preventDefault();
            return false;
        }
    });
});

And this other too, as you suggested, for the source-list (after create event)

$('#tokenfield').on('tokenfield:createdtoken tokenfield:removedtoken', function (event) {
    var field = $(this);
    var currentTokens = field.tokenfield('getTokens').map(function(i){return i.value});
    var originalSource = field.data('bs.tokenfield').options.autocomplete.source;
    var newSource = [];
    for (var i = 0; i<originalSource.length; i++) {
      if(currentTokens.indexOf(originalSource[i])==-1){
        newSource.push(originalSource[i]);
      }
    };
    //update source
    field.data('bs.tokenfield').$input.autocomplete({source: newSource});
    //empty the input field
    $(".tokenfield.form-control").find("input.token-input").val("");
});

NOTE: I changed the "check loop", (double for was overkilling), and added a check to avoid "capitalized" matching, just in case you need it.

function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}
like image 22
GrayFox Avatar answered Oct 26 '22 19:10

GrayFox