Атрибут VaadinSession и обновление связанных с сеансом компонентов
У меня есть Vaadin Navigator с несколькими элементами просмотра. Каждое представление имеет свою цель, однако некоторые также содержат общие черты, которые я поместил в пользовательские компоненты.
Одним из таких пользовательских компонентов является меню - оно расположено вверху и позволяет перемещаться между различными видами. Я создаю и добавляю этот компонент в конструктор каждого представления (если вы заинтересованы в реализации меню, смотрите конец этого поста). Вот скелет для каждого пользовательского представления:
class MyViewX implements View {
MenuViewComponent mvc;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
@Override
public void enter(ViewChangeEvent event) {
}
}
Все идет нормально. Чтобы упростить задачу, я объясню свою проблему с помощью простой метки, а не одного из других моих пользовательских компонентов, но зависимость, которую я опишу здесь, для этих компонентов такая же, как и с меткой.
Допустим, у меня есть ярлык, единственной целью которого является отображение приветствия с именем пользователя. Для этого я использую VaadinSession, где храню атрибут. Это делается моим LoginController, который проверяет пользователя, просматривая базу данных, и, если пользователь присутствует, атрибут устанавливается, и одно из представлений открывается автоматически. Проблема в том, что VaadinSession.getCurrent().getAttribute("username")
возвращается null
когда вызывается внутри конструктора. Это, конечно, имеет смысл, омхо, потому что конструктор не должен быть связан атрибутом сеанса.
До сих пор мне удалось использовать enter()
Метод, в котором нет проблем с получением атрибутов сеанса:
class MyViewX implements View {
MenuViewComponent mvc;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
@Override
public void enter(ViewChangeEvent event) {
String username = (String)VaadinSession.getCurrent().getAttribute("username");
Label greeting = new Label("Hello " + username);
addComponent(greeting);
}
}
Проблема, которая вытекает из этого, очевидна - всякий раз, когда я открываю представление, где присутствует этот ярлык, добавляется новый ярлык, поэтому, если я повторно посещу представление 10 раз, я получу 10 ярлыков. Даже если я переместу метку в переменную члена класса, addComponent(...)
это тот, который портит вещи. Некоторые из моих пользовательских компонентов действительно зависят от username
атрибут (для отображения пользовательского контента), следовательно, я также должен поместить их в enter(...)
метод. addComponent(...)
делает беспорядок из этого. Я даже попробовал грязный способ удаления компонента, а затем снова добавить его, увы! напрасно:
class MyViewX implements View {
MenuViewComponent mvc;
Label greeting;
public MyViewX() {
mvc = new MenuViewComponent();
addComponent(mvc);
}
@Override
public void enter(ViewChangeEvent event) {
String username = (String)VaadinSession.getCurrent().getAttribute("username");
greeting = new Label("Hello " + username);
// Remove if present
try { removeComponent(greeting); }
catch(Exception ex) { }
// Add again but with new content
addComponent(greeting);
}
}
но это все еще не работает. Итак, мой вопрос: каков самый простой способ обновления компонента, который требует привязанных к сеансу атрибутов?
Навигация через пользовательский компонент меню здесь не является проблемой, поскольку все компоненты меню загружаются в его конструктор. Вот почему он также загружает этот компонент, в частности, в собственный конструктор представления. Вот пример кнопки в моем меню, которая открывает вид:
@SuppressWarnings("serial")
@PreserveOnRefresh
public class MenuViewComponent extends CustomComponent {
public MenuViewComponent(boolean adminMode) {
HorizontalLayout layout = new HorizontalLayout();
Label title = new Label("<h2><b>Vaadin Research Project</b></h2>");
title.setContentMode(ContentMode.HTML);
layout.addComponent(title);
layout.setComponentAlignment(title, Alignment.TOP_LEFT);
Button personalDashboardButton = new Button("Personal dashboard", new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
getUI().getNavigator().navigateTo(MainController.PERSONALDASHBOARDVIEW);
}
});
personalDashboardButton.setStyleName(BaseTheme.BUTTON_LINK);
layout.addComponent(personalDashboardButton);
layout.setComponentAlignment(personalDashboardButton, Alignment.TOP_CENTER);
// Add other buttons for other views
layout.setSizeUndefined();
layout.setSpacing(true);
setSizeUndefined();
setCompositionRoot(layout);
}
}
PERSONALDASHBOARDVIEW
это только один из многих моих взглядов.
1 ответ
Возможно, стоит подумать о том, как долго экземпляры вашего представления должны "жить", так же, как долго они отображаются, до завершения сеанса или их сочетания. Имея это в виду и в зависимости от того, что должно произойти при вводе / повторном вводе представления, у вас есть как минимум следующие 3 варианта:
1) Воссоздать весь вид (с учетом раннего просмотра сбора мусора)
сначала зарегистрируйте ClassBasedViewProvider (вместо StaticViewProvider), который не содержит ссылок на созданные представления:
navigator = new Navigator(this, viewDisplay); navigator.addProvider(new Navigator.ClassBasedViewProvider(MyView.NAME, MyView.class));
простая реализация представления
public class MyView extends VerticalLayout implements View { public static final String NAME = "myViewName"; @Override public void enter(ViewChangeListener.ViewChangeEvent event) { // initialize tables, charts and all the other cool stuff addComponent(new SweetComponentWithLotsOfStuff()); } }
2) Сохраните некоторые уже созданные компоненты и замените другие
public class MyView extends VerticalLayout implements View {
private MySweetComponentWithLotsOfStuff mySweetComponentWithLotsOfStuff;
public MyView() {
// initialize only critical stuff here or things that don't change on enter
addComponent(new MyNavigationBar());
}
@Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// oh, so the user does indeed want to see stuff. great, let's do some cleanup first
removeComponent(mySweetComponentWithLotsOfStuff);
// initialize tables, charts and all the other cool stuff
mySweetComponentWithLotsOfStuff = new SweetComponentWithLotsOfStuff();
// show it
addComponent(mySweetComponentWithLotsOfStuff);
}
}
3) Ленивое создание и обновление (или нет) контента при входе
public class MyView extends VerticalLayout implements View {
private boolean isFirstDisplay = true;
private MySweetComponentWithLotsOfStuff mySweetComponentWithLotsOfStuff;
public MyView() {
// initialize only critical stuff here, as the user may not even see this view
}
@Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
// oh, so the user does indeed want to see stuff
if (isFirstDisplay) {
isFirstDisplay = false;
// lazily initialize tables, charts and all the other cool stuff
mySweetComponentWithLotsOfStuff = new SweetComponentWithLotsOfStuff();
addComponent(mySweetComponentWithLotsOfStuff);
} else {
// maybe trigger component updates, or simply don't do anything
mySweetComponentWithLotsOfStuff.updateWhateverIsRequired();
}
}
}
Я уверен (и любопытно), что могут быть и другие варианты, но я в основном использовал вариант 1) использование Spring с представлениями прототипов и вкладками компонентов.