Datasource query filter


(Михаил) #1

Доброго дня.
Правильно я понимаю, что такая функциональность в 7.0 отсутствует для новых источников?
https://doc.cuba-platform.com/manual-7.0/datasource_query_filter.html

Что-то предложите в замен?

Добавлю вопрос немного из другой темы, но тем не менее тоже ныне относящийся к legasy. @WindowParam теперь, как я понимаю, тоже устарел. Подскажите, а как в текущей парадигме решить следующую задачу?

Есть сущность А. В А есть атрибуты Country и Area (положим оба - pickerField). Необходимо, если выбрано значение в Country, при открытии лукапа Area отфильтровать все экземпляры в броуз-скрине по Country, а также при добавлении нового экземпляра Area устанавливать значение Country.


(Михаил) #2

Для таких же, как я, которые в танке.
Первая часть задачи (передача параметра в lookup-screen) решается как-то так:

    @Subscribe("partNumberField.lookup")
    private void onPartNumberFieldLookup(Action.ActionPerformedEvent event) {
        screenBuilders.lookup(Part.class, this)
                .withField(partNumberField)
                .withOptions(new MapScreenOptions(ImmutableMap.of("partType", 20)))
                .build()
                .show();
    }

Получение переданного параметра в browse-screen:

    @Subscribe
    private void onInit(InitEvent event) {
        final ScreenOptions options = event.getOptions();
        if (options instanceof MapScreenOptions){
            final int partType = ((int) ((MapScreenOptions) options).getParams().get("partType"));
            });
        }
    }

Возможно есть варианты элегантней.

По второй части задачи пока понимания нет. Можно в лоб, как я понимаю, получить список и отфильтровать:

partsDc.getMutableItems().stream.filter(...)

Но, кмк, должен существовать вариант с фильтрацией перед получением данных, а не после такового.


(Михаил) #3

Уважаемые разработчики платформы, выражаю признательность за обновление документации, в лице @krivopustov.
https://doc.cuba-platform.com/manual-7.0-ru/gui_data_comp_param.html

“Старый” механизм был очень хорош тем, что можно было передать параметр в экран или не передавать его…
В описанном примере в экран в любом случае должна быть установлена страна, иначе будет выброшено исключение. Что делать, если при использовании в качестве справочника, я не хочу передавать страну, а если в качестве лукапа - передаю? Подменять запрос в Dl на лету?
Была мысль использовать 2 Dl в одном Dc:

        <collection id="districtsDc"
                    class="com.borets.wedb.entity.District"
                    view="district-view-with-country">
            <loader id="districtsDl">
                <query>
                    <![CDATA[select e from wedb$District e]]>
                </query>
            </loader>
            <loader id="districtCountryDl">
                <query>
                    <![CDATA[select e from wedb$District e where e.country = :country]]>
                </query>
            </loader>
        </collection>
    @Subscribe
    private void onBeforeShow(BeforeShowEvent event) {
        final DataLoader districtsFilteredByCountryDl = getScreenData().getLoader("districtCountryDl"); // При использовании инжекта, картина не изменяется.
        if (this.country == null)
            districtsDl.load();
        else {
            districtsFilteredByCountryDl.setParameter("country", this.country);
            districtsFilteredByCountryDl.load();
        }
    }

Хотя в xsd я не получаю никаких ошибок, но в рантайм я получаю следующее исключение:

IllegalArgumentException: Loader 'districtCountryDl' not found

Полагаю, дело в том, что в этом методе вы не предполагали множественность лоадеров в контейнере.

    protected void loadCollectionContainer(ScreenData screenData, Element element, @Nullable ScreenData hostScreenData) {
        String containerId = getRequiredAttr(element, "id");

        CollectionContainer<Entity> container;

        if (checkProvided(element, hostScreenData)) {
            //noinspection ConstantConditions
            container = hostScreenData.getContainer(containerId);
        } else {
            container = factory.createCollectionContainer(getEntityClass(element));
            loadView(element, getEntityClass(element), container);
        }

        screenData.registerContainer(containerId, container);

        Element loaderEl = element.element("loader");
        if (loaderEl != null) {
            loadCollectionLoader(screenData, loaderEl, container, hostScreenData);
        }

        for (Element collectionEl : element.elements()) {
            loadNestedContainer(screenData, collectionEl, container, hostScreenData);
        }
    }

Не знаю где это может выстрелить дальше, но может быть имеет смысл регистрировать все лоадеры в контейнере? Или вы предложите другое решение?
Выстрелит, как минимум, в методе loadAll

Очередной UPD:
Пока сделал вот так, но это как-то не очень…

    @Subscribe
    private void onBeforeShow(BeforeShowEvent event) {
        if (this.country != null) {
            districtsDl.setQuery(districtsDl.getQuery() + " where e.country = :country");
            districtsDl.setParameter("country", this.country);
        }
        districtsDl.load();
    }