Взаимодействие родительского окна и фрагмента


(Михаил) #1

В справке о “старых” контроллерах есть вот такой замечательный параграф:
https://doc.cuba-platform.com/manual-7.0-ru/frame.html
Либо я пока плохо ориентируюсь в новой справке, либо такой параграф для новых экранов отсутствует.

Подскажите, каким образом в новой парадигме заставить фрагмент использовать датаконтейнер (DC) основного окна декларативно? Если во фрагменте указать <instance>, то он замаскирует DC родительского окна. Если не указывать, то при открытии сообщает об отсутствии такого DC.
Или это необходимо делать только программно?
Если так, то все равно хотелось бы пример. Желательно, чтобы фрагмент сам устанавливал экземпляр в своем DC из окна владельца.


(Михаил) #2

Уважаемые разработчики платформы и более опытные коллеги.
Скажите, молчание по данному вопросу можно интерпретировать, как то, что я просил какую-то глупость? Или никто не работает с фрагментами?


(Юрий Артамонов) #4

Здравствуйте,

Вы можете использовать контейнер родительского окна во фрагментах, если объявите его с атрибутом provided:

<data>
    <instance id="clientDc"
              class="com.company.demo.entity.Client"
              view="_local"
              provided="true"/>
</data>

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

<form id="form" dataContainer="clientDc">
    <column width="250px">
        <textField id="discountField" property="discount"/>
    </column>
</form>

Кроме того, в версии 7 появилась возможность подписываться на события родительского окна в контроллере фрагмента, например, на инициализацию сущности редактора:

@UiController("demo_DiscountFragment")
@UiDescriptor("discount-fragment.xml")
public class DiscountFragment extends ScreenFragment {
    @Subscribe(target = Target.PARENT_CONTROLLER)
    private void onInit(StandardEditor.InitEntityEvent<Client> event) {
        event.getEntity().setDiscount(10);
    }
}

P.S. Прошу заметить, что это бесплатный форум и вся команда CUBA Platform старается ответить на ваши вопросы. Мы будем очень благодарны, если вы поможете нам, отвечая на вопросы сообщества. Если вам требуется дополнительная помощь или гарантированное время ответа, мы будем рады предложить вам услуги коммерческой поддержки.


(Михаил) #6

Благодарю, Павел.
Вы меня очень выручили,

К сожалению, справка об этом молчит.

Очень круто. Скажите, это может быть вынесено в окно Subscribe to Event студии?

Павел, я прекрасно это понимаю и благодарен за вашу поддержку.


(Kirill Khoroshilov) #7

Михаил!

Павел не Павел, а Юрий!
:slight_smile:


(Михаил) #8

Кирилл, спасибо за замечание! Конечно же, благодарность Юрию. Хотя Павлу я тоже благодарен :blush:
А Михаилу больше не наливать…


(Михаил) #9

Юрий, к сожалению не взлетело.
При установки у DC атрибута provided="true" получаю следующую ошибку:

Сведения
java.lang.IllegalStateException: Host ScreenData is null
	at com.haulmont.cuba.gui.model.impl.ScreenDataXmlLoader.checkProvided(ScreenDataXmlLoader.java:471)
	at com.haulmont.cuba.gui.model.impl.ScreenDataXmlLoader.loadInstanceContainer(ScreenDataXmlLoader.java:96)
	at com.haulmont.cuba.gui.model.impl.ScreenDataXmlLoader.load(ScreenDataXmlLoader.java:79)
	at com.haulmont.cuba.gui.xml.layout.loaders.WindowLoader.loadScreenData(WindowLoader.java:173)
	at com.haulmont.cuba.gui.xml.layout.loaders.WindowLoader.loadComponent(WindowLoader.java:71)
	at com.haulmont.cuba.web.sys.WebScreens.loadWindowFromXml(WebScreens.java:355)
	at com.haulmont.cuba.web.sys.WebScreens.createScreen(WebScreens.java:227)
	at com.haulmont.cuba.web.sys.WebScreens.create(WebScreens.java:171)
	at com.haulmont.cuba.gui.builders.EditorBuilderProcessor.createScreen(EditorBuilderProcessor.java:231)
	at com.haulmont.cuba.gui.builders.EditorBuilderProcessor.buildEditor(EditorBuilderProcessor.java:81)
	at com.haulmont.cuba.gui.builders.EditorBuilder.build(EditorBuilder.java:335)
	at com.haulmont.cuba.gui.actions.list.EditAction.actionPerform(EditAction.java:139)
	at com.haulmont.cuba.web.gui.components.WebAbstractTable.handleClickAction(WebAbstractTable.java:1186)
	at com.haulmont.cuba.web.gui.components.WebAbstractTable.lambda$initComponent$db766529$1(WebAbstractTable.java:997)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:496)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:273)
	at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:237)
	at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:1041)
	at com.vaadin.v7.ui.Table.handleClickEvent(Table.java:3126)
	at com.vaadin.v7.ui.Table.changeVariables(Table.java:2918)
	at com.haulmont.cuba.web.widgets.CubaTable.changeVariables(CubaTable.java:326)
	at com.haulmont.cuba.web.widgets.CubaGroupTable.changeVariables(CubaGroupTable.java:298)
	at com.vaadin.server.communication.ServerRpcHandler.changeVariables(ServerRpcHandler.java:611)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocation(ServerRpcHandler.java:457)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:400)
	at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:260)
	at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:82)
	at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:40)
	at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1577)
	at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:425)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.serviceAppRequest(CubaApplicationServlet.java:329)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.service(CubaApplicationServlet.java:215)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:107)
	at jespa.http.HttpSecurityService.doFilter(HttpSecurityService.java:1596)
	at com.borets.wedb.web.JespaAuthProvider.doFilter(JespaAuthProvider.java:94)
	at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:112)
	at org.springframework.web.filter.CompositeFilter.doFilter(CompositeFilter.java:73)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.doFilter(CubaHttpFilter.java:108)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:748)

Исключение бросается в следующем методе:

    protected boolean checkProvided(Element element, ScreenData hostScreenData) {
        boolean provided = Boolean.parseBoolean(element.attributeValue("provided"));
        if (provided && hostScreenData == null) {
            throw new IllegalStateException("Host ScreenData is null");
        }
        return provided;
    }

В него приходит provided=true и hostScreenData=null. Прошелся выше по стеку и вижу, что в классе WindowLoader метод

    protected void loadScreenData(Window window, Element element) {
        Element dataEl = element.element("data");
        if (dataEl != null) {
            ScreenDataXmlLoader screenDataXmlLoader = beanLocator.get(ScreenDataXmlLoader.class);
            ScreenData screenData = UiControllerUtils.getScreenData(window.getFrameOwner());
            screenDataXmlLoader.load(screenData, dataEl, null);

            ((ComponentLoaderContext) context).setScreenData(screenData);
        }
    }

Явно вызывает screenDataXmlLoader.load(screenData, dataEl, null);, c параметрами, в которых hostScreenData=null


(Юрий Артамонов) #10

Скорее всего, ваш родительский экран базируется на AbstractWindow и старой UI инфраструктуре, или же в нём действительно нет элемента <data>.

Пример использования, который я привёл выше, можно найти здесь: https://github.com/cuba-labs/editor-fragment

По вашему описанию сложно понять, что пошло не так. Пожалуйста, прикрепляйте код, лучше всего в виде простой ссылки на Github репозиторий или ZIP архив с исходниками, достаточно простого тестового проекта без деталей вашей предметной области. На вопросы, полезные для всего сообщества, мы отвечаем в первую очередь.


(Михаил) #11

Юрий, сам экран наследник StanardEditor - создан через plugin cuba (New -> Screen), а фрейм наследник ScreenFragment.

Условие выполняется, если элемент data наоборот не пуст.

Я сейчас попробую на новом проекте.

UPD: вопрос решился. Сэмпл очень помог. Была путаница какой из dc должен быть provided.


(Михаил) #12

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


(Юрий Артамонов) #13

Так всё верно, ведь это событие StandardEditor.InitEntityEvent<Client>, а не событие Screen.InitEvent.


(Михаил) #14

Юрий, извинюсь, но еще вопрос.

Правильно я понимаю, что “первое” событие контроллера в котором я могу получить экземпляр редактируемого элемента это AfterShowEvent? Просто у меня есть какое-то внутреннее противоречие между “после показа” и например, изменением видимости поля (в зависимости от значения атрибута), которое вроде бы правильно сделать “до показа”.
UPD: Пожалуй, сниму вопрос. В BeforeShowEvent тоже можно получить экземпляр сущности, но не из DC, а с помощью метода getEditedEntity().