Вопрос "пересоздания сущности"

Добрый день, пытаюсь понять как реализовать определенный паттерн действий для моей сущности, мне нужно чтоб при нажатии кнопки edit в меню, открывалось окно с полями этой сущности но кнопка была не изменить а сохранить, то есть изменить сущность было нельзя а только создать еще одну по ее образу и подобию но с немного другими значениями в полях, на усмотрение пользователя, если я правильно понял, нужно сделать кастомный Action верно?

Добрый день

Все верно, для подобного поведения требуется определить свой action с кастомным поведением

Вот пример логики для сущности User, который может вам помочь:

    @Autowired
    protected ScreenBuilders screenBuilders;
    @Autowired
    protected GroupTable<User> usersTable;

    @Subscribe("usersTable.copyNew")
    public void onUsersTableCopyNew(final Action.ActionPerformedEvent event) {
        screenBuilders.editor(usersTable)
                .newEntity()
                .withInitializer(user -> {
                    //ваша логика по заполнению полей новой сущности
                    User selectedUser = usersTable.getSingleSelected();
                    if (selectedUser == null) {
                        return;
                    }

                    user.setUsername(selectedUser.getUsername());
                    user.setFirstName(selectedUser.getPassword());
                    user.setLastName(selectedUser.getLastName());
                    //...
                })
                .show();
    }

copy-and-save-example

С уважением,
Дмитрий

Сделал в точно как у вас на скрине, только со своими кнопками и классом

Пишет что нет подходящего бина с названием GroupTable<Мой Класс>
Прикладываю текст ошибки

NoSuchBeanDefinitionException: No qualifying bean of type ‘io.jmix.ui.component.GroupTable<com.infratech.infratechadminpanel.entity.license.License>’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Могу приложить фото класса если необходимо

Подскажите, пожалуйста, какую версию фреймворка вы используете?

Код контроллера и дескриптора будет полезен для решения проблемы.

Версия 1.4.4

Код Экшена:

@ActionType("ren")
public class Renewal<E> extends ItemTrackingAction {

    @Autowired
    protected ScreenBuilders screenBuilders;

    @Autowired
    protected GroupTable<License> licenseGroupTable;

    public Renewal(String id) {
        super(id);
    }


    @Subscribe("licenseGroupTable.copyNew")
    public void onUsersTableCopyNew(final Action.ActionPerformedEvent event) {
        screenBuilders.editor(licenseGroupTable)
                .newEntity()
                .withInitializer(license -> {
                    License selectedLicense = licenseGroupTable.getSingleSelected();
                    if (selectedLicense == null) {
                        return;
                    }

                    license.setInn(selectedLicense.getInn());
                })
                .show();
    }
}

Код xml дескриптора

<window xmlns="http://jmix.io/schema/ui/window"
        xmlns:c="http://jmix.io/schema/ui/jpql-condition"
        caption="msg://licenseBrowse.caption"
        focusComponent="licensesTable"
        focusMode="NO_FOCUS">
    <data readOnly="true">
        <collection id="licensesDc"
                    class="com.entity.license.License">
            <fetchPlan extends="_base"/>
            <loader id="licensesDl">
                <query>
                    <![CDATA[select e from License e]]>
                </query>
            </loader>
        </collection>
    </data>
    <facets>
        <dataLoadCoordinator auto="true"/>
        <screenSettings id="settingsFacet" auto="true"/>
    </facets>
    <actions>
        <action id="lookupSelectAction"
                caption="msg:///actions.Select"
                icon="LOOKUP_OK"
                shortcut="${COMMIT_SHORTCUT}"/>
        <action id="lookupCancelAction"
                caption="msg:///actions.Cancel"
                icon="LOOKUP_CANCEL"/>
    </actions>
    <dialogMode height="600"
                width="800"/>
    <layout expand="licensesTable" spacing="true">
        <filter id="filter"
                dataLoader="licensesDl">
            <properties include=".*"/>
        </filter>
        <groupTable id="licensesTable"
                    width="100%"
                    dataContainer="licensesDc">
            <actions>
                <action id="create" type="create">
                    <properties>
                        <property name="screenId" value="License.create"/>
                    </properties>
                </action>
                <action id="edit" type="edit">
                </action>
                <action id="remove" type="remove"/>
                <action id="renewal" type="ren"/>
            </actions>
            <columns>
                <column id="subject"/>
                <column id="inn"/>
                <column id="kpp"/>
                <column id="dateOfPurchase"/>
                <column id="expirationDate"/>
                <column id="licenseLevel"/>
                <column id="tools"/>
            </columns>
            <simplePagination/>
            <buttonsPanel id="buttonsPanel"
                          alwaysVisible="true">
                <button id="createBtn" action="licensesTable.create"/>
                <button id="editBtn" action="licensesTable.edit"/>
                <button id="removeBtn" action="licensesTable.remove"/>
                <button id="copyNew" action="licensesTable.renewal"/>
            </buttonsPanel>
        </groupTable>
        <hbox id="lookupActions" spacing="true" visible="false">
            <button action="lookupSelectAction"/>
            <button action="lookupCancelAction"/>
        </hbox>
    </layout>
</window>

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

Причина ошибки ясна.

Существует несколько способов описать логику для Action.
Можно определить его атрибуты в XML. И затем логику работы в контроллере экрана.
Либо полноценное описание, чтобы можно было использовать определение Action через атрибут type.

Я предоставил пример кода для простого определения Action в контроллере экрана (UiController).
В вашем случае вы пытаетесь определить новый класс Action со своей логикой.

Уточните, пожалуйста, какой способ реализации Вам подходит больше?

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

Если Вы хотите именно определить новый Action, я могу предоставить новый пример.

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

Перевел логику в класс Экрана (UiController) все еще падает ошибка,

DevelopmentException: Unable to find an instance of type ‘interface io.jmix.ui.component.GroupTable’ named ‘licenseGroupTable’ for instance of ‘com.infratech.infratechadminpanel.screen.license.LicenseBrowse’

Я написал небольшой пример класса Action.
В данной реализации он должен работать с любой сущностью, без каких-либо дополнительных настроек.

Однако я не проверял досконально :grin:.

Код для Action
import io.jmix.core.MetadataTools;
import io.jmix.core.entity.EntityValues;
import io.jmix.core.metamodel.model.MetaClass;
import io.jmix.ui.ScreenBuilders;
import io.jmix.ui.action.ActionType;
import io.jmix.ui.action.ItemTrackingAction;
import io.jmix.ui.component.Component;
import io.jmix.ui.component.data.meta.EntityDataUnit;
import io.jmix.ui.icon.Icons;
import io.jmix.ui.icon.JmixIcon;
import org.springframework.beans.factory.annotation.Autowired;

@ActionType(CopyNewAction.ID)
public class CopyNewAction extends ItemTrackingAction {

    protected static final String ID = "copyNew";

    protected ScreenBuilders screenBuilders;
    protected MetadataTools metadataTools;

    public CopyNewAction(String id) {
        super(id);
        setCaption("Copy to new");
    }

    @Autowired
    public void setScreenBuilders(ScreenBuilders screenBuilders) {
        this.screenBuilders = screenBuilders;
    }

    @Autowired
    public void setMetadataTools(MetadataTools metadataTools) {
        this.metadataTools = metadataTools;
    }

    @Autowired
    protected void setIcons(Icons icons) {
        this.icon = icons.get(JmixIcon.COPY);
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public void actionPerform(Component component) {
        if (target == null) {
            throw new IllegalStateException("CopyNewAction target is not set");
        }

        if (!(target.getItems() instanceof EntityDataUnit)) {
            throw new IllegalStateException("CopyNewAction target dataSource is null or does not implement EntityDataUnit");
        }

        MetaClass metaClass = ((EntityDataUnit) target.getItems()).getEntityMetaClass();
        if (metaClass == null) {
            throw new IllegalStateException("Target is not bound to entity");
        }

        Object targetEntity = target.getSingleSelected();
        if (targetEntity == null) {
            throw new IllegalStateException("There is not selected item in CopyNewAction target");
        }

        screenBuilders.editor(target)
                .newEntity(copyEntity(targetEntity, metaClass))
                .show();
    }

    protected Object copyEntity(Object targetEntity, MetaClass metaClass) {
        Object fakeEntityForId = metadata.create(metaClass);
        Object newEntity = metadataTools.deepCopy(targetEntity);
        EntityValues.setId(newEntity, EntityValues.getId(fakeEntityForId));

        return newEntity;
    }
}

GIF-пример

copy-new-test

Надеюсь этот пример поможет.

С уважением,
Дмитрий

1 симпатия

У вас в дескрипторе у таблицы id=licensesTable, а в коде вы написали licenseGroupTable

1 симпатия

Да все работает,
На всякий случай замечу для других пользователей, что логика из дискриптора едитора будет передаваться если вы пользуйтесь этим экшеном

Спасибо за помощь Дмитрий.
Хорошего Дня)

Благодарю, Ярослав, действительно, не обратил на это внимание.