4.3.2. Передача параметров в экран

Передача параметров из одного экрана в другой является одной из самых частых задач в разработке UI. Рассмотрим типовые решения этой задачи на примере демо-приложения "управление заказами".

При открытии экрана методом openWindow

Параметры могут быть переданы в мэп, являющейся опциональным параметром методов openWindow(), openLookup() и openEditor(). Они будут доступны в открываемом экране в виде мэп, передаваемой в метод init(), а также индивидуально, если они инжектируются с помощью аннотации @WindowParam.

Предположим, что мы хотим отфильтровать список продуктов в экране просмотра сущности Product, передав в него некоторые параметры из экрана редактирования сущности Order.

  • Экран редактирования OrderEdit содержит метод addOrderLine(), вызываемый действием addOrderLine. Этот метод открывает экран выбора продукта, передавая в него два параметра:

    • текущий выбранный покупатель,

    • список уже добавленных продуктов.

      Когда пользователь выбирает продукт, открывается экран QuantityDialog, где пользователь вводит количество единиц выбранного продукта. После закрытия этого экрана создаётся новый экземпляр сущности OrderLine и затем добавляется в таблицу строк заказа.

      openLookup("sample$Product.browse",
              items -> {
                  if (!items.isEmpty()) {
                      openQuantityDialog((Product) items.iterator().next());
                  }
              },
              WindowManager.OpenType.THIS_TAB,
              ParamsMap.of(
                      "customer", getItem().getCustomer(),
                      "added", orderLinesDs.getItems().stream()
                                              .map(line -> line.getProduct().getId())
                                              .collect(Collectors.toList())
              )
      );
  • Экран просмотра ProductBrowse изменяет свой источник данных в зависимости от переданного покупателя. Если покупатель передан, в таблице отображаются только продукты, имеющие ссылку на этого покупателя либо не связанные ни с одним покупателем. Параметры инжектируются в контроллер экрана с помощью аннотации @WindowParam:

    @WindowParam
    private Customer customer;
    
    @Override
    public void init(Map<String, Object> params) {
        if (customer != null) {
            productsDs.setQuery(
                    "select e from sample$Product e left join e.customer c " +
                    "where c.id = :param$customer or c is null");
        }
    }

    Также, когда экран выбора продукта открывается для создания строк заказа, в нём программно создаётся и применяется фильтр, который отображает только те продукты, которые ещё не были использованы в этом заказе.

    Tip

    Фильтры нужно создавать в методе ready(), так как на момент вызова метода init() фильтры ещё не инициализированы.

    @WindowParam
    private List<UUID> added;
    
    @Override
    public void ready() {
        if (added != null && !added.isEmpty()) {
            FilterEntity filterEntity = metadata.create(FilterEntity.class);
            filterEntity.setName("Not added yet");
            filterEntity.setXml("<filter>\n" +
                    " <and>\n" +
                    " <c name=\"id\" class=\"java.util.UUID\" inExpr=\"true\" hidden=\"true\" operatorType=\"NOT_IN\" width=\"1\" type=\"PROPERTY\">" +
                    " <![CDATA[((e.id not in :component$filter.id_list) or (e.id is null)) ]]>\n" +
                    " <param name=\"component$filter.id_list\" javaClass=\"java.util.UUID\">NULL</param>\n" +
                    " </c>\n" +
                    " </and>\n" +
                    "</filter>");
            filter.setFilterEntity(filterEntity);
            filter.setParamValue("id_list", added);
            filter.apply(true);
        }
    }

    Содержимое атрибута FilterEntity.xml может быть взято из фильтра, созданного при работе приложения: откройте Entity Inspector, найдите созданный фильтр, сохраненный как экземпляр сущности sec$Filter, и скопируйте его XML.

При открытие экранов из PickerField

Компонент PickerField и компоненты, его расширяющие, также могут передавать параметры в открываемые экраны. Параметры задаются для действий PickerField: LookupAction и OpenAction.

Допустим, мы хотим изменять заголовок экрана выбора сущности Customer, если он был открыт из компонента PickerField в экране редактирования сущности Product.

  • В экране ProductEdit мы указываем параметры для действия PickerField LookupAction, используя метод setLookupScreenParams():

    public class ProductEdit extends AbstractEditor<Product> {
    
        @Named("fieldGroup.customer")
        private PickerField customerField;
    
        @Override
        protected void postInit() {
            customerField.getLookupAction().setLookupScreenParams(ParamsMap.of("product", getItem()));
        }
    }
  • Затем мы инжектируем переданные параметры в экране CustomerBrowse:

    @WindowParam
    private Product product;
    
    @Override
    public void init(Map<String, Object> params) {
        if (product != null && product.getName() != null) {
            getFrame().setCaption("Select a customer for " + product.getName());
        }
    }

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