Select2 with createSearchChoice uses newly created choice for keyboard entry even given a match, bug or am I missing something?

你离开我真会死。 提交于 2019-11-27 14:54:41

问题


I'm using Select2 (version 3.4.0) to populate a tag list. The tags are matched against existing ones via ajax call, and I'm using createSearchChoice to allow creating new tags. The code works so far, and looks something like this:

$(mytags).select2({
    multiple: true,
    placeholder: "Please enter tags",
    tokenSeparators: [ "," ],
    ajax: {
        multiple: true,
        url: myurl,
        dataType: "json",
        data: function(term, page) {
            return {
                q: term
            };
        },
        results: function(data, page) {
            return data;
        }
    },
    createSearchChoice: function(term) {
        return {
            id: term,
            text: term + ' (new)'
        };
    },
});

All pretty standard, except note the appended (new) in createSearchChoice. I need users to know that this is not a preexisting tag.

It works as expected: if I start typing "new-tag", I get "new-tag (new)" tag suggested at the top of the list, and if I pick it, the tag list contains "new-tag (new)", as expected. If the tag already exists, Select2 detects the match, and no "(new)" choice is created. Pressing return or clicking on the match works as expected.

The problem appears when I type a comma (my single tokenSeparators entry) while there is a match. Select2 closes that token, and adds the tag to the list, but with the "(new)" label appended, i.e. it uses the return value from createSeachChoice even if it does not have to.

Is this a bug in Select2, or am I using it wrong (and what should I do instead)?


回答1:


I 'm not sure if this is a bug or not -- in any case, there is no open issue referring to this behavior at the GitHub issue tracker at this moment.

You can mostly fix the behavior yourself though. The idea is that the createSearchChoice callback must be able to tell if term refers to a search result or not. But createSearchChoice does not have direct access to the search results, so how can we enable that? Well, by saving the latest batch of search results from inside the results callback.

var lastResults = [];

$(...).select2({
    ajax: {
        multiple: true,
        url: "/echo/json/",
        dataType: "json",
        type: "POST",
        data: function (term, page) {
            return {
                json: JSON.stringify({results: [{id: "foo", text:"foo"},{id:"bar", text:"bar"}]}),
                q: term
            };
        },
        results: function (data, page) {
            lastResults = data.results;
            return data;
        }
    },
    createSearchChoice: function (term) {
        if(lastResults.some(function(r) { return r.text == term })) {
            return { id: term, text: term };
        }
        else {
            return { id: term, text: term + " (new)" };
        }
    }
});

This code uses Array.some so you need something better than IE8 (which is the select2 minimum requirement) to run it, but of course it is possible to emulate the behavior.

See it in action.

There is, however, a fly in the ointment: this code works correctly only if the search results corresponding to the current search term have been already received.

This should be obvious: if you type very fast and create a search term that corresponds to an existing tag but hit comma before the search results that include that tag have arrived, createSearchChoice will be testing for the tag's presence among the previously received search results. If those results do not include the tag, then the tag will be displayed as "new" even though it is not.

Unfortunately I don't believe there is anything you can do to prevent this from happening.




回答2:


Instead of tweeking the result, I think it is better to work on the server side.

If the server doesn't find a tag make it return a json answer with the new tag

{"more":false,"results":[{"id":"whatever","text":"new-tag (new)"}]}



回答3:


There is another parameter for the 'createSearchChoice' - 'page', it lists all the choices, you can easily find dupes with it.

createSearchChoice = function (term, page) {
    if( page.some(function(item) {
        return item.text.toLowerCase() === term.toLowerCase();
    }) ){
        return { val: term, name: term + '*' };
    }
}


来源:https://stackoverflow.com/questions/16606092/select2-with-createsearchchoice-uses-newly-created-choice-for-keyboard-entry-eve

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!