Select2 с createSearchChoice использует только что созданный выбор для ввода с клавиатуры, даже учитывая совпадение, ошибку или я что-то не хватает?

Я использую Select2 (версия 3.4.0) для заполнения списка тегов. Теги сопоставлены с существующими через ajax-вызов, и я использую createSearchChoice, чтобы разрешить создание новых тегов. Код работает до сих пор и выглядит примерно так:

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

Все довольно стандартно, за исключением добавления (new) в createSearchChoice. Мне нужно, чтобы пользователи знали, что это уже не существующий тег.

Он работает так, как ожидалось: если я начну вводить "новый тег", я получаю "новый тег (новый)", который предлагается в верхней части списка, и, если я его выберу, список тегов содержит "new- тег (новый)", как и ожидалось. Если тег уже существует, Select2 определяет совпадение, и не создается "(новый)" выбор. Нажатие на возврат или щелчок по совпадению работает как ожидалось.

Проблема возникает, когда я ввожу запятую (моя единственная запись tokenSeparators), когда есть совпадение. Select2 закрывает этот токен и добавляет тег в список, но с добавленной меткой "(new)", то есть использует возвращаемое значение из createSeachChoice, даже если это не обязательно.

Является ли это ошибкой в ​​Select2, или я использую ее неправильно (и что мне делать вместо этого)?

Ответ 1

Я не уверен, является ли это ошибкой или нет - в любом случае нет открытой проблемы, связанной с этим поведением в трекер-проблеме GitHub в данный момент.

В большинстве случаев вы можете исправить свое поведение. Идея состоит в том, что обратный вызов createSearchChoice должен быть способен определить, ссылается ли term на результат поиска или нет. Но createSearchChoice не имеет прямого доступа к результатам поиска, поэтому как мы можем это включить? Ну, сохраняя последнюю партию результатов поиска внутри обратного вызова results.

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)" };
        }
    }
});

Этот код использует Array.some, поэтому вам нужно что-то лучше IE8 (что является минимальным требованием select2) для его запуска, но, конечно, можно эмулировать поведение.

Посмотрите на действие.

Есть, однако, муха в мазе: этот код работает правильно, только если результаты поиска, соответствующие текущему поисковому запросу, уже получены.

Это должно быть очевидно: если вы набираете очень быстро и создаете условие поиска, которое соответствует существующему тегу, но ударяете запятую перед результатами поиска, которые включают этот тег, createSearchChoice будет тестировать наличие тега среди ранее полученные результаты поиска. Если эти результаты не включают тег, то тег будет отображаться как "новый", даже если это не так.

К сожалению, я не верю, что вы можете что-то сделать, чтобы это не происходило.

Ответ 2

Вместо того, чтобы поддразнивать результат, я думаю, что лучше работать на стороне сервера.

Если сервер не находит тег, он возвращает ответ json с новым тегом

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

Ответ 3

Есть еще один параметр для 'createSearchChoice' - 'page', в нем перечислены все варианты, вы можете легко найти обманы с ним.

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