Fork me on GitHub

Как этим пользоваться?

Вы можете скачать последнюю версию библиотеки в разделе Downloads её проекта на github и добавить её в свой проект. Для корректной работы библиотеки, естественно, требуется наличие в проекте jar-файла совместимой версии mvp4g и, на данный момент, gwt-log.

Потенциально библиотека вполне надёжна для использования — сложно-составной пример работает стабильно, а ядро этой идеи (правда, устаревшая и невылизанная версия — актуальную встроить пока нет возможности) работает в нашем проекте.

Поясню, как пользоваться всем этим кодом, если вы решили разработать на его основе проект. Ниже представлены одновременно последовательные и отдельные сценарии — выполнив их по очереди (на некоторых переходя в цикл “повторять несколько раз пока необходимо”) можно написать всё приложение целиком, но и после этого каждое из действий можно выполнять отдельно, по мере потребности изменений в приложении.

Словами описать можно отнюдь не всё, иногда удобнее взглянуть по ссылкам на примеры в приведённой очередности. Не проходите мимо этой возможности :).

1. Каркас приложения

  1. Продумать систему навигации вашего проекта, желательно чтобы она ложилась на схему тип-объекта/действие.
  2. Создать четыре enum-файла. Эти четыре enum-файла — главная и единственная конфигурация структуры страниц и лэйатуов в проекте (они не должны сразу же отражать действительность, но что-то потенциально реальное в них всё же должно быть):
  3. Создать EntryPoint вашего проекта и зарегистрировать в нём описанные порталы, причём выполнить это до запуска модуля mvp4g. В дальнейшем тут же нужно будет регистрировать новые лэйауты и лэйаут-билдеры, но в данный момент это необязательно. см. точку входа LayoutingDemo.java в примере.
  4. Создать Presenter главной страницы и отнаследовать его от AMainPresenter. Ничего больше особенного здесь делать не нужно. см. page/main/presenter/MainPresenter.java в примере.
  5. Создать View главной страницы и отнаследовать его от AMainView (см. page/main/view/MainView.java в примере). Методы, которые требуется определить:
  6. Создать главную шину событий, имплеменитирующую IsMainEventBus. В главной шине нужно переопределить все методы IsMainEventBus и перенаправить их в презентер главной страницы . Также нужно создать главный модуль, ничем не отличающийся от главного модуля mvp4g. см. page/main/MainEventBus.java и page/main/MainModule.java в примере.

2a. Создание лэйаута

  1. Добавьте идентификатор нового лэйаута в enum лэйатуов. см. в примере id/L.java.
  2. Если нужно, добавьте идентификаторы новых плейсходеров в enum плейсхолдеров. см. в примере id/O.java.
  3. Если вы хотите описать лэйаут декларативно, создайте соответствующий ui.xml. В этом ui.xml для обозначения мест, куда будут вставляться портлеты (плейсхолдеров) потребуется использовать виджет Outlet. см. layout/LayoutItem.ui.xml в примере. Можно описывать лэйаут и не декларативно, тогда придётся создавать плэйсхолдеры используя конструктор Outlet. Не забудьте, что вам нужен плейсходер для информировании о статусе.
  4. Создайте класс лэйаута, отнаследовав его от Layout. В родительский конструктор передайте новый идентификатор лэйаута и идентификаторы всех плейсходеров, принадлежащих этому лэйауту. В переопределённом методе prepareOutlet() возвратите нужный Outlet по переданному идентификатору. Виджет должен инициироваться прямо в конструкторе. см. layout/LayoutItem.java в примере.
  5. Зарегистрируйте лэйаут в точке входа через метод Layouts.register(). см. LayoutingDemo.java:47 в примере.

2b. Создание лэйаута с состояниями

  1. Добавьте идентификатор нового лэйаута в enum лэйатуов. см. в примере id/L.java.
  2. Если нужно, добавьте идентификаторы новых плейсходеров в enum плейсхолдеров. Туда же добавьте плейсхолдер для информирования о состоянии (я назвал его STATUS), если вы его ещё не завели. см. в примере id/O.java.
  3. Если вы хотите описать лэйаут декларативно, создайте соответствующий ui.xml. В этом ui.xml для обозначения мест, куда будут вставляться портлеты (плейсхолдеров) потребуется использовать виджет Outlet. Для места, куда будет вставляться сообщение о состоянии тоже подготовьте плэйсхолдер. см. layout/LayoutEdit.ui.xml в примере. Можно описывать лэйаут и не декларативно, тогда придётся создавать плэйсхолдеры используя конструктор Outlet.
  4. Создайте класс лэйаута, отнаследовав его от LayoutWithState. В родительский конструктор передайте новый идентификатор лэйаута и идентификаторы всех плейсходеров, принадлежащих этому лэйауту. Отдельно (последним параметром) передайте идентификатор плэйсхолдера, предназначенного для сообщения о состоянии. В переопределённом методе prepareOutlet() возвратите нужный Outlet по переданному идентификатору. В методе prepare(State) вы можете по переданному состоянию переключать видимость тех или иных виджетов внутри лэйаута и/или делать что-то ещё, если нужно. Виджет должен инициироваться прямо в конструкторе. см. layout/LayoutEdit.java в примере.
  5. Зарегистрируйте лэйаут в точке входа через метод Layouts.register(). см. LayoutingDemo.java:47 в примере.

3. Создание группы страниц (модуля)

  1. Добавьте идентификатор новой группы в enum для групп. см. в примере id/G.java.
  2. Создайте модуль для группы, ничем не отличающийся от модуля mvp4g. см. в примере page/user/UserModule.java. Не забудьте добавить его в ChildModules главной шины событий. см. в примере page/main/MainEventBus.java.
  3. Создайте шину событий для вашей группы и отнаследуйте её от ChildEventBus. см. в примере page/user/UserEventBus.java.
  4. Создайте HistoryConverter и отнаследуйте его от PortalsHistoryConverter. Передайте в родительский конструктор идентификатор группы. Метод convertFromUrl предназначен для того, чтобы по полученным PortalUrl/Portal (используйте метод P.by() из пункта 1.2.1) вызвать нужный метод в шине событий. см. в примере page/user/history/UserHistoryConverter.java.
  5. Создайте своего наследника LayoutBuilder для соответствующей шины событий. Метод layout() вставляет портлеты в плейсходеры и вовращает true, если всё прошло удачно. здесь также удобно использовать метод P.by(). см. в примере page/user/layout/UserLayoutBuilder.java.
  6. Зарегистрируйте билдер в точке входа через метод LayoutBuilders.register(). см. LayoutingDemo.java:62 в примере.

4a. Создание цельной страницы без поддержки состояний

  1. Добавьте описание доступа по URL к странице и укажите идентификатор соответствующего ей лэйаута в enum для страниц. см. в примере id/P.java.
  2. Зарегистрируйте навигационное событие для вашей страницы в шине событий соответствующего модуля. см. в примере методы news(), edit() и show() в интерфейсе page/news/NewsEventBus.java.
  3. В метод convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/news/history/NewsHistoryConverter.
  4. Создайте Presenter вашей страницы и отнаследуйте его от класса PortalPresenter. Интерфейс View должен расширять интерфейс IsPortalView. В конструктор нужно передать идентификатор портала, которому соответствует презентер. У презентера, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/news/presenter/NewsEditPresenter.java.
  5. Создайте View вашей страницы, отнаследовав его от класса Portal. Для каждой части страницы (виджета), которая будет вставлена в отдельный плейсходер, нужно использовать оборачивающий виджет Plug, независимо от того, используете вы ui.xml или нет. Корневым элементом для view должен быть виджет Plugs, который позволяет перечислить внутри нёго несколько виджетов Plug. см. в примере page/news/view/NewsEditView.java и page/news/view/NewsEditView.ui.xml, в случае NewsEditView в разные плейсхолдеры будут вставлены infoPlug (блок с информацией) и savePlug (кнопка “Save”).
  6. На основе выбранного вами лэйаута добавьте сборку страницы в LayoutBuilder вашего модуля. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета, который будет вставляться в плейсхолдер. Эти события должны уходить в презентер, созданный в п.3 и вызывать в нём метод plug(Place, view.get...), физически вставляя виджеты в плейсхолдеры. Для этого создайте в презентере методы, перехватывающие эти события и вызывающие plug() с нужной частью view. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров. см. в примере page/news/layout/NewsLayoutBuilder.java, page/news/NewsEventBus.java и page/news/presenter/NewsEditPresenter.java, для NEWS_EDIT это методы plugItemEditor и plugSaveButton.

4b. Создание страницы с портлетами без поддержки состояний

  1. Добавьте описание доступа по URL к странице и укажите идентификатор соответствующего ей лэйаута в enum для страниц. см. в примере id/P.java.
  2. Зарегистрируйте навигационное событие для вашей страницы в шине событий соответствующего модуля. см. в примере методы news(), edit() и show() интерфейса page/news/NewsEventBus.java.
  3. В метод convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/news/history/NewsHistoryConverter.
  4. Создайте для каждого портлета свой Presenter и отнаследуйте каждый от класса PortletPresenter. Интерфейс View у них должен расширять интерфейс IsPortletView. У презентера, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/news/presenter/NewsInfoPresenter.java, page/news/presenter/NewsListPresenter.java и page/news/presenter/UserCardPresenter.java.
  5. View каждого из портлетов должен наследоваться от виджета Portlet. Корневым элементом для этих view должен быть виджет Plug. см. в примере page/news/view/NewsListView.java, page/news/view/NewsListView.ui.xml, page/news/view/NewsInfoView.java, page/news/view/NewsInfoView.ui.xml и page/news/view/UserCardView.java.
  6. На основе выбранного вами лэйаута добавьте сборку страницы в LayoutBuilder вашего модуля. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета, который будет вставляться в плейсхолдер. Эти события должны уходить в презентеры, созданные в п.3 и вызывать в них отнаследованный метод plug(Place), физически вставляя виджеты в плейсхолдеры. Поскольку в данном случае view однозначен, переопределять в презентерах ничего не нужно. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров. см. в примере page/news/layout/NewsLayoutBuilder.java и page/news/NewsEventBus.java. Для NEWS_SHOW это методы plugNewsInfo, plugUserCard и plugTestWidget. Для NEWS_LIST это методы plugNewsList, plugUserCard и plugTestWidget.

4c. Создание цельной страницы с поддержкой состояний

  1. Добавьте описание доступа по URL к странице и укажите идентификатор соответствующего ей лэйаута в enum для страниц. см. в примере id/P.java.
  2. Зарегистрируйте навигационное событие для вашей страницы в шине событий соответствующего модуля. см. в примере методы users(), edit() и show() интерфейса page/user/UserEventBus.java.
  3. В метод convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/user/history/UserHistoryConverter.
  4. Создайте Presenter вашей страницы и отнаследуйте его от класса StatedPortalPresenter. Интерфейс View должен расширять интерфейс IsStatedPortalView. В конструктор нужно передать идентификатор портала, которому соответствует презентер. Для изменения состояния вида достаточно вызвать в необходимый момент подходящий метод у проперти state (например, state.noData(), state.loading(), state.noMatches(), state.gotData()…). По умолчанию виды с состояниями имеют состояние LOADING. У презентера, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/user/presenter/UserEditPresenter.java.
  5. Создайте View вашей страницы, отнаследовав его от класса StatedPortal. Для каждой части страницы (виджета), которая будет вставлена в отдельный плейсходер, нужно использовать оборачивающий виджет Plug, независимо от того, используете вы ui.xml или нет. Корневым элементом для view должен быть виджет Plugs, который позволяет перечислить внутри нёго несколько виджетов Plug. Для каждого из необходимых состояний вида (можно регистрировать/создавать не все) также надо создать отдельный Plug. После этого, используя метод register(State, Plug), их нужно связать с соответствующим им состоянием в методе createView см. в примере page/user/view/UserEditView.java и page/user/view/UserEditView.ui.xml, в случае UserEditView в разные плейсхолдеры будут вставлены infoPlug (блок с информацией), avatarPlug, agePlug и testPlug (просто фраза “Test Widget”); состояниям NO_DATA и LOADING соответствуют блоки ifEmpty и whenLoading, они регистрируются в методе createView
  6. На основе выбранного вами лэйаута добавьте сборку страницы в LayoutBuilder вашего модуля. В метод layout кроме всего прочего приходит состояние страницы state, его можно использовать для построения различных вариантов страницы, лэйаут уже подготовлен. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета и для каждого нужного вам состояния, которые будет вставляться в плейсхолдеры. Эти события должны уходить в презентер, созданный в п.3 и вызывать в нём метод plug(Place, view.get...), физически вставляя виджеты в плейсхолдеры или уже определённые методы plugEmpty/plugNoMatches/plugLoading для состояний. Для этого создайте в презентере методы, перехватывающие события виджетов и вызывающие plug() с нужной частью view. Для событий “подключения” состояний ничего создавать не нужно. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров (плейсхолдер состояния для виджета состояния). см. в примере page/user/layout/UserLayoutBuilder.java ветка USER_EDIT, page/user/UserEventBus.java и page/user/presenter/UserEditPresenter.java, для USER_EDIT это методы plugInfoEditor, plugAgeEditor, plugAvatarEditor и plugTestWidget.

4d. Создание страницы из портлетов, в которой некоторые или все портлеты поддерживают состояния

  1. Добавьте описание доступа по URL к странице и укажите идентификатор соответствующего ей лэйаута в enum для страниц. см. в примере id/P.java.
  2. Зарегистрируйте навигационное событие для вашей страницы в шине событий соответствующего модуля. см. в примере методы users(), edit() и show() интерфейса page/user/UserEventBus.java.
  3. В метод convertFromUrl() у HistoryConverter вашего модуля добавьте вызов этого события, если по истории был получен ваш портал. Создайте в конвертере метод on... для вашего события и возвратите отконвертированные параметры, если они есть — для удобства можно использовать проперти url конвертера. см. в примере page/user/history/UserHistoryConverter.
  4. Создайте для каждого портлета свой Presenter и отнаследуйте каждый от класса StatedPortletPresenter (или от PortletPresenter, если у виджета не должно быть состояний). Интерфейс View у них должен расширять интерфейс IsStatedPortletView (или IsPortletView, если у виджета нет состояний). Для изменения состояния вида портлетов достаточно вызвать в необходимый момент подходящий метод у проперти state (например, state.noData(), state.loading(), state.noMatches(), state.gotData()…). По умолчанию виды с состояниями имеют состояние LOADING. У презентеров, как и у конвертера, также есть проперти url, вы можете использовать его для построения URL и передаче их во view. см. в примере page/user/presenter/UserAvatarPresenter.java, page/user/presenter/UserDetailsPresenter.java, page/user/presenter/UserInfoPresenter.java и page/user/UserListPresenter.
  5. View каждого из портлетов должен наследоваться от виджета StatedPortlet (или Portlet, если виджет не имеет состояний). Корневым элементом для view с состоянием должен быть виджет Plugs, внутри которого должны находиться виджеты Plug с основным и побочными необходимыми вам состояниями. Для того, чтобы связать виджеты состояний с соответствующими им состояниями, используйте метод register(Plug, State) в реализации createView(). Для view без состояний, корневым элементом должен быть Plug, здесь всё проще — он и будет главным видом. см. в примере виды с состояниями: page/user/view/UserAvatarView.java, page/user/view/UserAvatarView.ui.xml, page/user/view/UserDetailsView.java, page/user/view/UserDetailsView.ui.xml, page/user/view/UserInfoView.java, page/user/view/UserInfoView.ui.xml, виды без состояний: page/news/view/NewsInfoView.java, page/news/view/NewsInfoView.ui.xml
  6. На основе выбранного вами лэйаута добавьте сборку страницы в LayoutBuilder вашего модуля. Для сборки зарегистрируйте в шине событий модуля по одному новому событию plug... (принимающему в параметре Place) для каждого виджета, который будет вставляться в плейсхолдер. Эти события должны уходить в презентеры, созданные в п.3 и вызывать в них отнаследованный метод plug(Place), физически вставляя виджеты в плейсхолдеры. Поскольку в данном случае view однозначен, переопределять в презентерах ничего не нужно. Затем, в LayoutBuilder-е, вызовите созданные события поочерёдно, передавая в параметре соответствующие идентификаторы плейсхолдеров. см. в примере page/user/layout/UserLayoutBuilder.java и page/user/UserEventBus.java. Для USER_SHOW это методы plugUserInfo, plugUserAvatar и plugUserDetails. Для USERS_LIST это методы plugUserInfo, plugUserAvatar и plugUsersList.

[Содержание]