Вопрос по <fragment> и передача в него параметров

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

host screen:

....
<data>
        <instance id="productsDc"
                  class="com.company.appjmix.entity.products.Products">
            <fetchPlan extends="_base"/>
            <loader/>
        </instance>
</data>
....
<layout>
.....
 <form id="form" dataContainer="productsDc">
  <textField id="nameField" property="name" />
</from>
 <fragment id="unitSpec_fragment" screen="UnitsSpecFragment">
                    <properties>
                        <property name="products" ref="productsDc"/>
                    </properties>
                </fragment>
.....
</layout>

fragment screen:

<fragment xmlns="http://jmix.io/schema/ui/fragment">
    <data>
        <instance id="productsDc" class="com.company.appjmix.entity.products.Products"
                  provided="true">
            <fetchPlan extends="_base"/>
        </instance>

        <collection id="unitsSpecDc" class="com.company.appjmix.entity.units.UnitsSpec" >
            <fetchPlan extends="_base"/>
            <loader id="unitsSpecDl">
               <query>
                <![CDATA[ select e from UnitsSpec e where e.ref_product = :refProd ]]>
               </query>
             </loader>
        </collection>


    </data>
    <layout>
        <textField id="nameProducts" caption="Товар"
                   dataContainer="productsDc" property="name"/>

        <table id="table" width="100%" dataContainer="unitsSpecDc">
            <columns>
                <column id="code"/>
                <column id="name"/>
                <column id="koef"/>
                <column id="active"/>
                <column id="ref_units.name"/>
                <column id="ref_product.name"/>
            </columns>
        </table>
    </layout>
</fragment>

Код фрагмента:

@UiController("UnitsSpecFragment")
@UiDescriptor("units-spec-fragment.xml")
public class UnitsSpecFragment extends ScreenFragment {
    private static final Logger log = LoggerFactory.getLogger(UnitsSpecFragment.class);

    @Autowired
    private InstanceContainer<Products> productsDc;
    @Autowired
    private CollectionLoader<UnitsSpec> unitsSpecDl;

    public void setProducts(InstanceContainer<Products> products) {
        log.info("setProducts in");
        /*  метод вызывается, но контейнер пуст productsDc, пуст  
            так же  products.getItem() - тоже null
       */
            
        log.info("id Products :{}",productsDc.getItem().getId().toString());

        //unitsSpecDl.setParameter("refProd",productsDc.getItem().getId());
        unitsSpecDl.load();
    }
}

Вопрос: как правильно инициализировать коллекцию productsDc в фрагменте, исходя из переданного параметра из хост-экрана?

Пример в документации как-то не понятен…
image

В итоге, должно получиться как-то так: во вкладке фрагмент с единицами измерения:
image

Возможно, есть какой-то рецепт для решения подобных задач по встраиванию в интерфейс подчиненных таблиц.

Так вы же определяете что productsDc provided. Зачем его еще и как параметр передавать? Контейнер пуст скорее всего потому-что экран еще не начинал грузить данные.

Скорее всего здесь products не нужно передавать во фргмент. Просто во фрагменте подпишитесь ItemChangeEvent productsDc и там вызывайте загрузку unitsSpecDl.

Или использовать префикс container_ тогда и кода никакого не нужно писать.

Или в Products можно добавить поле с маппингом UnitsSpec и сделать так как расписано в этом вопросе. То есть использовать CollectionPropertyContainer и сделать unitsSpecDc provided во фрагменте.

Спасибо за ответ! Буду пробовать. Очень не хватает примеров-рецептов для решения типовых задач для новичков в Jmix-е.

Спасибо. Разобрался, сделал с помощью префикса “container_”
Итог под спойлером.

Спойлер

Товары Products-edit

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://jmix.io/schema/ui/window"
        xmlns:c="http://jmix.io/schema/ui/jpql-condition"
        caption="msg://productsEdit.caption"
        focusComponent="form">
    <data>
        <instance id="productsDc"
                  class="com.company.appjmix.entity.products.Products">
            <fetchPlan extends="_base"/>
            <loader id="productsDl"/>
        </instance>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
        <screenSettings id="settingsFacet" auto="true"/>
    </facets>
    <actions>
        <action id="windowCommitAndClose"
                caption="msg:///actions.SaveClose"
                icon="EDITOR_OK"
                primary="true"
                shortcut="${COMMIT_SHORTCUT}"/>
        <action id="windowCommit"
                caption="msg:///actions.Save"
                icon="SAVE"/>
        <action id="windowClose"
                caption="msg:///actions.Close"
                icon="EDITOR_CANCEL"/>
    </actions>
    <dialogMode height="90%"
                width="AUTO"
                modal="true"
                forceDialog="true"/>
    <layout spacing="true" expand="tabProductSheets">
        <tabSheet id="tabProductSheets" >
            <tab id="tabProducts" expand="scrollBox"
                 caption="msg:///tabProducts.caption"
                 margin="true"
                 spacing="true">
                <scrollBox id="scrollBox" spacing="true">
                     <form id="form" dataContainer="productsDc">
                        <column width="350px">
                    <textField id="codeField" property="code" caption="msg://codeField.caption"/>
                    <textField id="nameField" property="name" caption="msg://nameField.caption"/>
                    <textField id="barcodeField" property="barcode" caption="msg://barcodeField.caption"/>
                    <entityPicker id="refGroupProductField" property="refGroupProduct"
                                  caption="msg://refGroupProductField.caption">
                        <actions>
                            <action id="entityLookup" type="entity_lookup"/>
                            <action id="entityClear" type="entity_clear"/>
                        </actions>
                    </entityPicker>
                    <entityPicker id="refUnitField" property="refUnit" caption="msg://refUnitField.caption">
                        <actions>
                            <action id="entityLookup" type="entity_lookup"/>
                            <action id="entityClear" type="entity_clear"/>
                        </actions>
                    </entityPicker>
                </column>
                     </form>
                </scrollBox>
            </tab>
            <tab id="tabUnitsProducts"
                 caption="msg:///tabUnitsProducts.caption"
                 margin="true"
                 spacing="true">
                <fragment id="unitSpecFragment" screen="UnitsSpecFragment">
                    <properties>
                        <property name="productsContainer" ref="productsDc"/>
                    </properties>
                </fragment>
            </tab>
        </tabSheet>
        <hbox id="editActions" spacing="true">
            <button id="commitAndCloseBtn" action="windowCommitAndClose"/>
            <button id="commitBtn" action="windowCommit"/>
            <button id="closeBtn" action="windowClose"/>
        </hbox>
    </layout>
</window>

Фрагмент с единицами измерения UnitsSpec с сылкой на Products

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<fragment xmlns="http://jmix.io/schema/ui/fragment">
    <data>
        <instance id="productsDc"  class="com.company.appjmix.entity.products.Products" provided="true"/>
        <collection id="unitsSpecDc" class="com.company.appjmix.entity.units.UnitsSpec" >
            <fetchPlan extends="_base"/>
            <loader id="unitsSpecDl">
               <query>
                <![CDATA[ select e from UnitsSpec e where e.ref_product = :container_productsDc ]]>
               </query>
             </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
    </facets>
    <layout>
        <dataGrid id="table" width="100%" dataContainer="unitsSpecDc">
            <actions>
                <action id="create" type="create"/>
                <action id="edit" type="edit"/>
                <action id="remove" type="remove"/>
            </actions>
            <buttonsPanel id="buttonsPanel" alwaysVisible="true">
                <button id="createBtn" action="table.create"/>
                <button id="editBtn" action="table.edit"/>
                <button id="removeBtn" action="table.remove"/>
            </buttonsPanel>
            <columns>
                <column id="code" property="code"/>
                <column id="name" property="name"/>
                <column id="koef" property="koef"/>
                <column id="active" property="active"/>
            </columns>
        </dataGrid>
    </layout>
</fragment>

В класс UnitsSpec добавлена процедура для вызова экрана редактирования и занесение ссылки на Products по умолчанию.

@UiController("UnitsSpecFragment")
@UiDescriptor("units-spec-fragment.xml")
public class UnitsSpecFragment extends ScreenFragment {

    @Autowired
    private InstanceContainer<Products> productsDc;

    @Autowired
    protected ScreenBuilders screenBuilders;

    @Subscribe("table.create")
    public void onTableCreate(Action.ActionPerformedEvent event) {

        Screen unitsSpecEditor = screenBuilders.editor(UnitsSpec.class, this)
                .newEntity()
                .withInitializer(unitsSpec -> {
                    unitsSpec.setRef_units(productsDc.getItem().getRefUnit());
                    unitsSpec.setRef_product(productsDc.getItem());
                })
                .withOpenMode(OpenMode.DIALOG)
                .build();

        unitsSpecEditor.addAfterCloseListener(afterCloseEvent -> {
            if (afterCloseEvent.closedWith(StandardOutcome.COMMIT)) {
                getScreenData().loadAll();
            }
        });

        unitsSpecEditor.show();
    }
}

Результат Товар:
image

Вкладка “Отпускные единицы измерения”
image

Однако, возникает ошибка:
при занесении новой записи в Products (без нажатия кнопки Сохранить) и внесении в каталог новой отпускный единицы UnitSpec, и затем - “сохранить и выйти” или “сохранить”, возникло такое исключение:
image

Консоль при ошибке:

Call: INSERT INTO Products (ID, BARCODE, CODE, CREATED_BY, CREATED_DATE, DELETED_BY, DELETED_DATE, LAST_MODIFIED_BY, LAST_MODIFIED_DATE, NAME, VERSION, REF_GROUP_PRODUCT_ID, REF_UNIT_ID, REF_ORGANIZATION_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
	bind => [c4ea6485-4ea5-31f3-3777-d9f66355ec9f, 2341234234, 5, admin, 2023-01-27 09:23:03.012, null, null, null, 2023-01-27 09:23:03.012, �����, 1, 26a98b50-a4b6-e4c6-e957-9d8c21fe69a1, 75aaac41-33e2-9e7e-f2f8-214f3d026bca, null]
Query: InsertObjectQuery(com.company.appjmix.entity.products.Products-c4ea6485-4ea5-31f3-3777-d9f66355ec9f [new,managed])

При этом, если закрыть экран и на вопрос, сохранять данные или нет, нажать “Нет”, Записи создаються.
Если Products был создан ранее, и производиться просто пополнение UnitSpec - то ошибка уже не возникает.
Код интерфейсов приведен выше - под спойлером.

Помогите, пожалуйста.

Так. Тут видимо проблема в том. Что сущности из фрагмента сохранятся перед основной. И при сохранении основной уже падает ошибка что она есть.

скорее всего нужно добавить .withParentDataContext(dataContext)
Чтоб UnitsSpec и Product сохранялись в одном коммите.
При этом убрать addAfterCloseListener с перезагрузкой данных. и в screenBuilders.editor передавать таблицу.

У меня в тестовом проекте сделано так. Все нормально сохраняется.

    @Subscribe("table.create")
    private fun onTableCreate(event: Action.ActionPerformedEvent) {
        screenBuilders.editor(table)
            .newEntity()
            .withInitializer {
                it.testEntity1 = productsDc.item
            }.withParentDataContext(dataContext)
            .build()
            .show()
    }

Все получилось, спасибо!