Select2 — сортировка результатов по запросу

Я использую Select2 версии 4.0.0.

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

Например, пользователь вводит «яблоко», и мои результаты таковы:

  1. "бананово-апельсиновое яблоко"
  2. "бананово-яблочно-апельсиновый"
  3. "яблочно-банановый апельсин"

Тогда «яблоко, банан, апельсин» должно появиться первым в списке результатов select2, потому что это результат, в котором «яблоко» появляется раньше других. Меня не так волнует порядок в прошлом.

Что мне переопределить или настроить, чтобы получить что-то подобное? Кажется, что matcher не обрабатывает порядок, а sorter не содержит данных запроса.


person nnyby    schedule 13.08.2015    source источник
comment
Может быть проще упорядочить элементы в исходном списке. И предположительно они будут отображаться как отсортированные, так и при фильтрации.   -  person Panoptik    schedule 14.08.2015
comment
Я не понимаю вашего комментария. Откуда мне знать, как упорядочить список до того, как пользователь начнет поиск?   -  person nnyby    schedule 14.08.2015
comment
Я имею в виду. Какой список заказов вы получите при поиске, если вы изначально отсортируете список по алфавиту?   -  person Panoptik    schedule 14.08.2015
comment
Порядок во время поиска по умолчанию в алфавитном порядке. Вот что я хотел бы изменить.   -  person nnyby    schedule 14.08.2015
comment
Вы загружаете свои опции через AJAX?   -  person Jasper    schedule 18.08.2015
comment
Я не думаю, что это полностью ясно, вы хотите, чтобы результаты отображались первыми, если термин начинается с того, что вы ищете?   -  person johnny 5    schedule 18.08.2015


Ответы (3)


Вы можете получить поисковый запрос из значения поля ввода, сгенерированного Select2, идентифицируя его с классом select2-search__field. Это, вероятно, будет разбивать версии, но, поскольку они не предоставляют ловушку для получения запроса, потребуется какой-то взлом. Вы можете отправить задачу, чтобы они добавили поддержку доступа к запросу во время сортировки, тем более что похоже, это было возможно в Select2 3.5.2.

$('#fruit').select2({
  width: '200px',
  sorter: function(results) {
    var query = $('.select2-search__field').val().toLowerCase();
    return results.sort(function(a, b) {
      return a.text.toLowerCase().indexOf(query) -
        b.text.toLowerCase().indexOf(query);
    });
  }
});
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" />

<select id="fruit">
  <option>Banana Orange Apple</option>
  <option>Banana Apple Orange</option>
  <option>Apple Banana Orange</option>
  <option>Achocha Apple Apricot</option>
  <option>Durian Mango Papaya</option>
  <option>Papaya</option>
  <option>Tomato Papaya</option>
  <option>Durian Tomato Papaya</option>
</select>

person heenenee    schedule 19.08.2015
comment
Напомню, вы можете гарантировать, что text будет содержать поисковый запрос. Единственным исключением является использование пользовательского matcher, и в этот момент вы, скорее всего, знаете, что это за исключения. - person Kevin Brown; 20.08.2015
comment
Спасибо за комментарий @KevinBrown. Я поверю вам на слово, что text будет содержать поисковый запрос, поэтому я соответственно упростил код. - person heenenee; 20.08.2015
comment
Спасибо, это почти работает, но, похоже, не сортирует весь список. Например, если у меня есть несколько результатов, где яблоко является первым словом, он поместит один вверху, а затем оставит другие результаты не отсортированными под результатами, которые не так важны. Сейчас я пробую метод @KevinBrown. - person nnyby; 24.08.2015
comment
@nnbyХа. Вы хотите сопоставлять только целые слова? Например, если вы ищете apple и у вас есть два результата: banana orange apple и horseapple tomato, хотите ли вы, чтобы banana orange apple отображалось первым? - person heenenee; 25.08.2015
comment
@heenenee, нет, полное совпадение слов не так важно, но оно не должно вести себя так: i.imgur.com/2JKKIOK.png Обратите внимание, что я искал proj, и он правильно помещает инструмент управления проектами на первое место, но на картинке вы не видите других результатов, таких как Project Rebirth и Project Релизы, которые должны находиться непосредственно под Инструментом управления проектами. - person nnyby; 25.08.2015
comment
@nnyby Вероятно, это потому, что вы ищете со строчными буквами p, но в результатах есть прописные P. Я изменил свой ответ, чтобы не учитывать регистр. Пожалуйста, попробуйте. - person heenenee; 25.08.2015
comment
@heenenee теперь отлично работает, спасибо! На самом деле я пробовал метод Кевина Брауна, который давал мне те же результаты, даже когда я добавил к нему нечувствительный к регистру хак, поэтому я собирался предположить, что у вас будет такое же поведение. Большое спасибо ... Я так долго возился с этим. - person nnyby; 25.08.2015

Проблема здесь в том, что Select2 в версии 4.0.0 отделил запрос результатов от их отображения. Из-за этого опция sorter, которую вы обычно используете для сортировки результатов, не проходит в сделанном запросе (который включает поисковый запрос).

Поэтому вам нужно найти способ кэшировать сделанный запрос, чтобы вы могли использовать его при сортировке. В моем ответе о подчеркивании условия поиска в результатах я кэширую запрос с помощью метода шаблонов loading, который всегда срабатывает всякий раз, когда выполняется поиск. Этот же метод можно использовать и здесь.

var query = {};

$element.select2({
  language: {
    searching: function (params) {
      // Intercept the query as it is happening
      query = params;

      // Change this to be appropriate for your application
      return 'Searching…';
    }
  }
});

Итак, теперь вы можете создать собственный метод sorter, который использует сохраненный query (и использует query.term в качестве условия поиска). В моем примере метода сортировки я использую позицию в тексте, где результат поиска должен быть отсортирован. Это, вероятно, похоже на то, что вы ищете, но это довольно грубый метод.

function sortBySearchTerm (results) {
  // Don't alter the results being passed in, make a copy
  var sorted = results.slice(0);

  // Array.sort is an in-place sort
  sorted.sort(function (first, second) {
    query.term = query.term || "";

    var firstPosition = first.text.toUpperCase().indexOf(
      query.term.toUpperCase()
    );
    var secondPosition = second.text.toUpperCase().indexOf(
      query.term.toUpperCase()
    );

    return firstPosition - secondPosition;
  });

  return sorted;
};

И это будет сортировать вещи так, как вы хотите это сделать. Вы можете найти полный пример со всеми частями, соединенными вместе ниже. Он использует три варианта примера, которые вы упомянули в своем вопросе.

var query = {};
var $element = $('select');

function sortBySearchTerm (results) {
  // Don't alter the results being passed in, make a copy
  var sorted = results.slice(0);
  
  // Array.sort is an in-place sort
  sorted.sort(function (first, second) {
    query.term = query.term || "";

    var firstPosition = first.text.toUpperCase().indexOf(
      query.term.toUpperCase()
    );
    var secondPosition = second.text.toUpperCase().indexOf(
      query.term.toUpperCase()
    );
    
    return firstPosition - secondPosition;
  });
  
  return sorted;
};

$element.select2({
  sorter: sortBySearchTerm,
  language: {
    searching: function (params) {
      // Intercept the query as it is happening
      query = params;

      // Change this to be appropriate for your application
      return 'Searching…';
    }
  }
});
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.css" rel="stylesheet"/>

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.js"></script>

<select style="width: 50%">
  <option>banana orange apple</option>
  <option>banana apple orange</option>
  <option>apple banana orange</option>
</select>

person Kevin Brown    schedule 19.08.2015

Нет необходимости сохранять срок:

$element.select2({
    sorter: function (data) {
        if(data && data.length>1 && data[0].rank){
            data.sort(function(a,b) {return (a.rank > b.rank) ? -1 : ((b.rank > a.rank) ? 1 : 0);} );
        }
        return data;
    }
    ,
    matcher:function(params, data) {
        // If there are no search terms, return all of the data
        if ($.trim(params.term) === '') {
          return data;
        }

        // Do not display the item if there is no 'text' property
        if (typeof data.text === 'undefined') {
          return null;
        }

        // `params.term` should be the term that is used for searching
        // `data.text` is the text that is displayed for the data object
        var idx = data.text.toLowerCase().indexOf(params.term.toLowerCase());
        if (idx > -1) {
          var modifiedData = $.extend({
            // `rank` is higher when match is more similar. If equal rank = 1
              'rank':(params.term.length / data.text.length)+ (data.text.length-params.term.length-idx)/(3*data.text.length)
          }, data, true);

          // You can return modified objects from here
          // This includes matching the `children` how you want in nested data sets
          return modifiedData;
        }

        // Return `null` if the term should not be displayed
        return null;
    }

})

person AlexPalla    schedule 25.04.2018