如何使用 Bootstrap Tokenfield 防止重复使用 Jquery Ui 自动完成

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

我正在尝试实施 Bootstrap Tokenfield with Jquery Ui autocomplete 到目前为止,我能够做到这一点,除了我无法防止输入字段中的重复,所以,不幸的是,我的用户可以选择相同的值两次。

在我的搜索中,我发现 Bootstrap Tokenfield 有一种 preventing duplicate 的方式。但是我不知道如何应用到我的代码,因为在我看来它与 Twitter typeahead 而不是 Jquery Ui.

如何使用 Jquery Ui 自动完成来防止与 Bootstrap TokenField 重复?

这是我的 Bootstrap TokenField 代码,基于 jquery ui 自动完成

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

    delay: 100
  },
  showAutocompleteOnFocus: true
});

以下是我在 Github 上找到的防止重复的内容,尽管我认为它适用于 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();
    });
});

我想你已经完成了,剩下要做的就是更换 class

所以在第一个代码之后,而不是第二个代码写

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

这里的不同之处在于必须应用您的 class,它适用于 Twitter Typeahead 和 Jquery Ui

这可以防止列出已添加为令牌的项目:

    $('.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})
})

创建或删除令牌后调用此函数以更新列表。它使用 JSON.stringify() 来比较对象,并对字符串对象和 {value: "foo", label: "bar"} 源对象进行比较。

@Javier 你的解决方案工作得很好,但有时它会出现错误并添加两倍的令牌!你知道这种行为吗?

PS 看了文档后我找到了解决办法。这两个事件处理都是需要的。因为事件在 creation/edit/remove 个令牌之前和之后触发。

所以你需要这个来防止添加(在创建事件之前)

$('#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;
        }
    });
});

正如您所建议的,还有另一个用于源列表(在创建事件之后)

$('#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("");
});

注意:我更改了 "check loop",(double for 太过分了),并添加了一个检查以避免 "capitalized" 匹配,以防万一你需要它。

function capitalizeFirstLetter(string) {
    return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
}