3.5.2.1.25. LookupField

Компонент для выбора значения из выпадающего списка. Выпадающий список реализует фильтрацию значений по мере ввода пользователя и постраничный вывод доступных значений.

gui lookupField

XML-имя компонента: lookupField.

  • Простейший вариант использования LookupField - выбор значения перечисления (enum) для атрибута сущности. Например, сущность Role имеет атрибут type типа RoleType, который является перечислением. Тогда для редактирования этого атрибута можно использовать LookupField следующим образом:

    <data>
        <instance id="roleDc"
                  class="com.haulmont.cuba.security.entity.Role"
                  view="_local">
            <loader/>
        </instance>
    </data>
    <layout expand="editActions" spacing="true">
        <lookupField dataContainer="roleDc" property="type"/>
    </layout>

    Как видно из примера, в экране описывается data container roleDc для сущности Role. В компоненте lookupField в атрибуте dataContainer указывается ссылка на источник данных, а в атрибуте property − название атрибута сущности, значение которого должно быть отображено. В данном случае атрибут является перечислением, и в выпадающем списке будут отображены локализованные названия всех значений этого перечисления.

  • Аналогично можно использовать LookupField для выбора экземпляра связанной сущности. Для формирования списка опций используется атрибут optionsContainer:

    <data>
        <instance id="carDc" class="com.haulmont.sample.core.entity.Car" view="carEdit">
            <loader/>
        </instance>
        <collection id="colorsDc" class="com.haulmont.sample.core.entity.Color" view="_minimal">
            <loader id="colorsDl">
                <query>
                    <![CDATA[select e from sample_Color e]]>
                </query>
            </loader>
        </collection>
    </data>
    <layout>
        <lookupField dataContainer="carDc" property="color" optionsContainer="colorsDc"/>
    </layout>

    В данном случае компонент отобразит имена экземпляров сущности Color, находящихся в коллекции данных colorsDs, а выбранное значение подставится в атрибут color сущности Car, находящейся в контейнере carDs.

    С помощью атрибута captionProperty можно указать, какой атрибут сущности использовать вместо имени экземпляра для строковых названий опций.

  • Метод setOptionCaptionProvider() позволяет задать заголовки для строковых названий опций, отображаемых компонентом LookupField:

    lookupField.setOptionCaptionProvider((item) -> item.getLocalizedName());
  • Список опций компонента может быть задан произвольно с помощью методов setOptionsList(), setOptionsMap() и setOptionsEnum(), либо с помощью XML-атрибута optionsContainer или optionsDatasource.

    • Метод setOptionsList() позволяет программно задать список опций компонента. Для этого объявляем компонент в XML-дескрипторе:

      <lookupField id="numberOfSeatsField" dataContainer="modelDc" property="numberOfSeats"/>

      Затем инжектируем компонент в контроллер и в методе onInit() задаем ему список опций:

      @Inject
      protected LookupField<Integer> numberOfSeatsField;
      
      @Subscribe
      public void onInit(InitEvent event) {
          List<Integer> list = new ArrayList<>();
          list.add(2);
          list.add(4);
          list.add(5);
          list.add(7);
          numberOfSeatsField.setOptionsList(list);
      }

      В выпадающем списке компонента отобразятся числа 2, 4, 5, 7. Выбранное число подставится в атрибут numberOfSeats сущности, находящейся в контейнере данных modelDс.

    • Метод setOptionsMap() позволяет задать строковые названия и значения опций по отдельности. Например, для описанного в XML-дескрипторе компонента numberOfSeatsField в методе onInit() контроллера задаем мэп опций:

      @Inject
      protected LookupField<Integer> numberOfSeatsField;
      
      @Subscribe
      public void onInit(InitEvent event) {
          Map<String, Integer> map = new LinkedHashMap<>();
          map.put("two", 2);
          map.put("four", 4);
          map.put("five", 5);
          map.put("seven", 7);
          numberOfSeatsField.setOptionsMap(map);
      }

      В выпадающем списке компонента отобразятся строки two, four, five, seven. Однако значением компонента будет число, соответствующее выбранной строке. Оно и подставится в атрибут numberOfSeats сущности, находящейся в контейнере данных modelDс.

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

  • setPopupWidth() позволяет установить ширину выпадающего списка, которая передается в метод в виде строки. Используя относительное значение (например, "50%"), можно установить ширину выпадающего списка относительно ширины LookupField. По умолчанию эта ширина имеет значение null, поэтому ширина выпадающего списка может быть больше ширины компонента, для того чтобы соответствовать содержимому всех отображаемых элементов. Установив значение "100%", ширина выпадающего списка будет равна ширине LookupField.

  • setOptionStyleProvider() позволяет задать отдельные стили для различных значений в выпадающем списке:

    lookupField.setOptionStyleProvider(entity -> {
        User user = (User) entity;
        switch (user.getGroup().getName()) {
            case "Company":
                return "company";
            case "Premium":
                return "premium";
            default:
                return "company";
        }
    });
  • Каждый элемент выпадающего списка может иметь значок слева. Чтобы установить значки, используйте метод setOptionIconProvider():

    lookupField.setOptionIconProvider(entity -> {
        if (entity.getType() == LegalStatus.LEGAL)
            return "icons/icon-office.png";
        return "icons/icon-user.png";
    });
    gui lookupField 2

    При использовании значков в формате SVG необходимо явно указывать их размеры, чтобы избежать наложения:

    <svg version="1.1"
         id="Capa_1"
         xmlns="http://www.w3.org/2000/svg"
         xmlns:xlink="http://www.w3.org/1999/xlink"
         xml:space="preserve"
    
         style="enable-background:new 0 0 55 55;"
         viewBox="0 0 55 55"
    
         height="25px"
         width="25px">
  • Если у компонента LookupField не установлен атрибут required, и если связанный атрибут сущности не объявлен как обязательный, то в списке опций компонента присутствует пустая строка, при выборе которой компонент возвращает значение null. Атрибут nullName позволяет задать строку, отображаемую в этом случае вместо пустой. Пример использования:

    <lookupField dataContainer="carDc" property="colour" optionsContainer="colorsDs" nullName="(none)"/>

    В данном случае вместо пустой строки отобразится строка (none), при выборе которой в связанный атрибут сущности подставится значение null.

    При программном задании списка опций методом setOptionsList() можно одну из опций передать в метод setNullOption(). Тогда при ее выборе пользователем значением компонента будет null.

    Фильтрация опций LookupField:
    • С помощью атрибута filterMode можно задать тип фильтрации опций при вводе пользователя:

      • NO − нет фильтрации.

      • STARTS_WITH − по началу фразы.

      • CONTAINS − по любому вхождению (используется по умолчанию).

    • Метод setFilterPredicate() позволяет настроить способ фильтрации. Предикат проверяет, совпадает ли введённая пользователем строка со заголовком элемента в списке, к примеру:

      BiFunction<String, String, Boolean> predicate = String::contains;
      lookupField.setFilterPredicate((itemCaption, searchString) ->
              predicate.apply(itemCaption.toLowerCase(), searchString));

      Функциональный интерфейс FilterPredicate содержит метод test, который позволяет реализовать более сложную логику фильтрации опций, например, игнорировать специальные символы или надстрочные знаки:

      lookupField.setFilterPredicate((itemCaption, searchString) ->
              StringUtils.replaceChars(itemCaption, "ÉÈËÏÎ", "EEEII")
                  .toLowerCase()
                  .contains(searchString));
  • Компонент LookupField способен обрабатывать ввод пользователя при отсутствии подходящей опции в списке. Для этого используется метод setNewOptionHandler(). Например:

    @Inject
    private Metadata metadata;
    @Inject
    private LookupField<Color> colorField;
    @Inject
    private CollectionContainer<Color> colorsDc;
    
    @Subscribe
    protected void onInit(InitEvent event) {
        colorField.setNewOptionHandler(caption -> {
            Color color = metadata.create(Color.class);
            color.setName(caption);
            colorsDc.getMutableItems()
                    .add(color);
            colorField.setValue(color);
        });
    }

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

    Вместо метода setNewOptionHandler() для обработки ввода пользователя можно использовать XML-атрибут newOptionHandler с указанным в нем методом контроллера. Данный метод должен иметь два параметра - первый типа LookupField, второй типа String. В них будут переданы соответственно экземпляр компонента и введенное пользователем значение. Атрибут newOptionAllowed используется, чтобы разрешить добавление новых опций.

  • XML-атрибут nullOptionVisible устанавливает видимость элемента со значением null в списке опций. Может работать только если атрибут required имеет значение false.

  • XML-атрибут textInputAllowed используется для отключения возможности фильтрации опций с клавиатуры. Это бывает удобно для коротких списков. Значение по умолчанию - true.

  • XML-атрибут pageLength позволяет переопределить количество опций на одной странице выпадающего списка, заданное свойством приложения cuba.gui.lookupFieldPageLength.

  • В веб-клиенте с темой, основанной на Halo, к компоненту LookupField можно применить предопределенные стили. Стили задаются в XML-дексрипторе или контроллере экрана с помощью атрибута stylename:

    <lookupField id="lookupField"
                 stylename="borderless"/>

    Чтобы применить стиль программно, выберите одну из констант класса HaloTheme с префиксом компонента LOOKUPFIELD_:

    lookupField.setStyleName(HaloTheme.LOOKUPFIELD_BORDERLESS);

    Стили компонента LookupField:

    • align-center - выравнивание текста по центру области.

    • align-right - выравнивание текста по правому краю области.

    • borderless - удаляет рамку и фон текстовой области.