VaadinSession属性和更新会话绑定组件

时间:2015-10-02 13:25:30

标签: vaadin custom-component vaadin-session

我有一个带有多个查看元素的Vaadin 导航器。每个视图都有不同的用途,但有些视图还包含我在自定义组件中放置的常见特征。

其中一个自定义组件是菜单 - 它位于顶部,允许在不同视图之间导航。我在每个视图的构造函数中创建并添加此组件(如果您对菜单的实现感兴趣,请参阅本文末尾)。以下是每个自定义视图的框架:

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。这当然是有道理的,因为构造函数不应该被session-attribute绑定。

到目前为止,我已设法使用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 个答案:

答案 0 :(得分:2)

可能值得考虑您的观看实例和#34;生活的时间长短,直到会话结束或两者混合为止。考虑到这一点,根据您输入/重新输入视图时需要发生的事情,您至少有以下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)的变体,使用带有原型视图和组件选项卡的弹簧。