Открытие экранов
Обычный (не корневой) экран можно открыть либо перейдя на его URL (указанный в аннотации @Route
), либо на текущей веб-странице, открыв диалоговое окно.
При использовании навигации, URL веб-браузера отражает состояние пользовательского интерфейса приложения, что позволяет использовать глубокие ссылки. Пользователь приложения может скопировать и поделиться адресом URL, тем самым предоставляя ссылку на определенный экран и его текущее состояние, например, на сведения о конкретном экземпляре сущности.
Минусом навигации является относительная сложность передачи параметров в открываемый экран. Их можно передавать только в URL в качестве параметров route или query. Кроме того, невозможно вернуть в вызывающий код какие-либо значения из экрана после его закрытия.
При открытии экрана в диалоговом окне URL браузера не изменяется, но зато экземпляр открытого экрана доступен вызывающему коду. Он позволяет передавать любые значения непосредственно в экземпляр экрана и возвращать результаты после закрытия экрана. Этот подход используется при открытии экранов списков для выбора и возврата экземпляров сущностей в компонентах выбора.
Главное меню открывает экраны с помощью навигации. Стандартные действия list_create и list_edit также открывают экраны деталей с помощью навигации, но их можно настроить на использование диалоговых окон. Стандартное действие entity_lookup всегда открывает экран списка в диалоговом окне, чтобы иметь возможность вернуть выбранные сущности.
Ниже описывается, как программно открывать экраны в коде приложения.
Навигация
Чтобы открыть экран с помощью навигации на его URL, инжектируйте бин ViewNavigators
и используйте его fluent-интерфейс для указания текущего экран и класса открываемого экрана:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToView() {
viewNavigators.view(this, MyOnboardingView.class).navigate();
}
navigate()
- это терминальный метод, выполняющий навигацию.
Если вызывающий класс не является экраном, и вы не можете передать this
в качестве текущего экрана, используйте UiComponentUtils.getCurrentView()
, чтобы найти открытый в данный момент экран.
Если необходимо вернуться к вызывающему экрану после закрытия открываемого экрана, вызовите метод withBackwardNavigation(true)
:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToViewThenBack() {
viewNavigators.view(this, MyOnboardingView.class)
.withBackwardNavigation(true)
.navigate();
}
Чтобы перейти к экрану списка, используйте метод listView()
, принимающий класс сущности. Класс экрана списка будет выбран по соглашению. Например:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToListView() {
viewNavigators.listView(this, Department.class).navigate();
}
Идентификатор или класс экрана списка можно указать явно, например:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToListViewWithClass() {
viewNavigators.listView(this, Department.class)
.withViewClass(DepartmentListView.class)
.navigate();
}
Чтобы перейти к экрану деталей, используйте метод detailView()
, принимающий класс сущности или визуальный компонент, привязанный к сущности. Класс экрана деталей будет выбран по соглашению.
Чтобы создать новый экземпляр сущности, вызовите newEntity()
. Например:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToCreateEntity() {
viewNavigators.detailView(this, Department.class)
.newEntity()
.navigate();
}
Чтобы отредактировать экземпляр сущности, вызовите editEntity()
. Например:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToEditEntity(Department entity) {
viewNavigators.detailView(this, Department.class)
.editEntity(entity)
.navigate();
}
Идентификатор или класс экрана деталей можно указать явно, используя методы withViewId()
и withViewClass()
.
Передача параметров
Рекомендуемый способ передачи параметров в экран, открываемый при навигации — использование метода withQueryParameters()
:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToViewWithQueryParameters() {
viewNavigators.view(this, FancyMessageView.class)
.withQueryParameters(QueryParameters.of("message", "Hello World!"))
.navigate();
}
В этом случае к URL будет добавлен параметр, например:
http://localhost:8080/FancyMessageView?message=Hello%20World!
В открываемом экране для получения значения параметра используйте обработчик QueryParametersChangeEvent
:
@ViewComponent
private H1 messageLabel;
public void setMessage(String message) {
messageLabel.setText(message);
}
@Subscribe
public void onQueryParametersChange(final QueryParametersChangeEvent event) {
List<String> messageParams = event.getQueryParameters().getParameters().get("message");
if (messageParams != null && !messageParams.isEmpty())
setMessage(messageParams.get(0));
}
Другой вариант — использовать метод withAfterNavigationHandler()
и передать параметр непосредственно объекту открываемого экрана:
@Autowired
private ViewNavigators viewNavigators;
private void navigateToViewWithAfterNavigationHandler() {
viewNavigators.view(this, FancyMessageView.class)
.withAfterNavigationHandler(afterViewNavigationEvent -> {
FancyMessageView view = afterViewNavigationEvent.getView();
view.setMessage("Hello World!");
})
.navigate();
}
В этом случае URL не будет содержать параметр:
http://localhost:8080/FancyMessageView
Этот подход проще и позволяет передавать сложные типы, но недостаток тот же, что и при открытии экранов в диалоговых окнах: он не обеспечивает глубокую ссылку, и состояние экрана будет потеряно, если пользователь обновит веб-страницу.
Открытие диалоговых окон
Бин DialogWindows
предоставляет удобный интерфейс для открытия экранов в диалоговых окнах. Его терминальные методы предоставляют доступ к экземпляру открываемого экрана, что позволяет передавать входные параметры непосредственно в экземпляр экрана и добавлять слушатели для получения результатов из открываемого экрана после его закрытия.
Чтобы открыть экран в виде диалогового окна, инжектируйте бин DialogWindows
и вызовите метод view()
, передав ему текущий экран и класс открываемого экрана. Затем вызовите терминальный метод open()
:
@Autowired
private DialogWindows dialogWindows;
private void openView() {
dialogWindows.view(this, MyOnboardingView.class).open();
}
Если вам нужно передать параметры в открываемый экран, вызовите терминальный метод build()
, задайте параметры для экрана, затем откройте диалоговое окно:
@Autowired
private DialogWindows dialogWindows;
private void openViewWithParams(String username) {
DialogWindow<MyOnboardingView> window =
dialogWindows.view(this, MyOnboardingView.class).build();
window.getView().setUsername(username);
window.open();
}
Чтобы получить результат из открываемого экрана после его закрытия, добавьте в диалоговое окно слушатель AfterCloseEvent
:
@Autowired
private DialogWindows dialogWindows;
private void openViewWithParamsAndResults(String username) {
DialogWindow<MyOnboardingView> window =
dialogWindows.view(this, MyOnboardingView.class).build();
window.getView().setUsername(username);
window.addAfterCloseListener(afterCloseEvent -> {
if (afterCloseEvent.closedWith(StandardOutcome.SAVE)) {
// ...
}
});
window.open();
}
Объект AfterCloseEvent
содержит действие закрытия (CloseAction
) переданное методу close()
экрана. Например, когда стандартный экран деталей сущности закрывается с помощью кнопки ОК , действие закрытия имеет значение save
. Вы можете проанализировать действие закрытия, используя методы getCloseAction()
или closedWith()
объекта события.
Слушатель AfterCloseEvent
можно также добавить, используя fluent-интерфейс:
@Autowired
private DialogWindows dialogWindows;
private void openViewWithResults() {
dialogWindows.view(this, MyOnboardingView.class)
.withAfterCloseListener(afterCloseEvent -> {
if (afterCloseEvent.closedWith(StandardOutcome.SAVE)) {
// ...
}
})
.open();
}
Чтобы выбрать сущности из экрана списка, откройте экран, используя метод lookup()
:
@Autowired
private DialogWindows dialogWindows;
private void openLookupView() {
dialogWindows.lookup(this, Department.class)
.withSelectHandler(departments -> {
// ...
})
.open();
}
Используйте метод withSelectHandler()
, чтобы предоставить лямбду, которая принимает коллекцию экземпляров сущностей, выбранных в открываемом экране.
Чтобы создать новый экземпляр сущности в экране деталей, укажите класс экрана и вызовите метод newEntity()
. Используйте слушатель AfterCloseEvent
, чтобы получить созданную сущность. Например:
@Autowired
private DialogWindows dialogWindows;
private void openDetailViewToCreateEntity() {
dialogWindows.detail(this, Department.class)
.withViewClass(DepartmentDetailView.class)
.newEntity()
.withAfterCloseListener(afterCloseEvent -> {
if (afterCloseEvent.closedWith(StandardOutcome.SAVE)) {
Department department = afterCloseEvent.getView().getEditedEntity();
// ...
}
})
.open();
}
Чтобы отредактировать существующую сущность в экране деталей, предоставьте экземпляр для редактирования, используя метод editEntity()
:
@Autowired
private DialogWindows dialogWindows;
private void openDetailViewToEditEntity(Department department) {
dialogWindows.detail(this, Department.class)
.withViewClass(DepartmentDetailView.class)
.editEntity(department)
.withAfterCloseListener(afterCloseEvent -> {
if (afterCloseEvent.closedWith(StandardOutcome.SAVE)) {
Department editedDepartment = afterCloseEvent.getView().getEditedEntity();
// ...
}
})
.open();
}
Входные параметры для экрана списка и экрана деталей можно предоставить так же, как описано для простого экрана в начале этого раздела: вызовите терминальный метод build()
, установите параметры экрана, затем откройте диалоговое окно.
Соглашения о выводе имен экранов
Экран списка или деталей можно вывести из класса сущности.
При переходе к экрану списка с помощью viewNavigators.listView(SomeEntity.class).navigate()
, фреймворк выбирает экран в следующем порядке:
-
Экран с идентификатором
SomeEntity.list
. -
Экран, помеченный
@PrimaryLookupView(SomeEntity.class)
. -
Экран с идентификатором
SomeEntity.lookup
.
При открытии экрана списка для поиска с помощью dialogWindows.lookup(this, SomeEntity.class).open()
, фреймворк выбирает экран в следующем порядке:
-
Экран, помеченный
@PrimaryLookupView(SomeEntity.class)
. -
Экран с идентификатором
SomeEntity.lookup
. -
Экран с идентификатором
SomeEntity.list
.
При переходе к экрану деталей или открытии его в диалоговом окне фреймворк выбирает экран в следующем порядке:
-
Экран, помеченный
@PrimaryDetailView(SomeEntity.class)
. -
Экран с идентификатором
SomeEntity.detail
.