确保 Vaadin 在单击添加的组件(例如 TabSheet 或 Tree ...)时加载最新数据

Ensure Vaadin load latest data in when clicking added components (such as TabSheet or Tree ...)

当我向Vaadin 的组件添加组件时(例如TabSheetTree),添加的组件被缓存。当用户单击选项卡(或树节点)时,如果它包含数据库数据,则会显示陈旧数据,而不反映最新的数据库状态。

请问有什么方法可以保证加载最新的数据吗?

我通过定义自定义界面解决了这个问题:

public interface Reloadable {
  void reload();
}

并且每个组件都实现了这个Reloadable接口,比如:

@SpringComponent
public class TeachersView extends VerticalLayout implements Reloadable, Serializable {

  @Inject
  private TeacherDao teacherDao;
  private final static int PAGESIZE = 10;

  private MTable<Teacher> mTable = new MTable<>(Teacher.class);

  @PostConstruct
  public void init() {
    // mTable settings skip here
    reload();
    addComponent(mTable);
  }

  @Override
  public void reload() {
    mTable.setBeans(new SortableLazyList<>(
      sortablePagingProvider ,
      () -> (int) teacherDao.count() ,
      PAGESIZE
    ));
  }

  private SortableLazyList.SortablePagingProvider<Teacher> sortablePagingProvider =
    (firstRow, asc, sortProperty) -> {  
      return teacherDao.findAll(
        new PageRequest(
          firstRow / PAGESIZE, PAGESIZE,
          asc ? Sort.Direction.ASC : Sort.Direction.DESC,
          sortProperty == null ? "id" : sortProperty
        )
      ).getContent();
    };
}

并将此视图注入 UI class :

@SpringUI(path = "/ui")
@Theme("valo")
public class VaadinUI extends UI {
  @Inject
  private TeacherDao teacherDao;

  @Inject
  private TeachersView teachersView;

  @Override
  protected void init(VaadinRequest vaadinRequest) {
    Panel panel = new Panel("Admin Panel");

    HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
    splitPanel.setSplitPosition(15, Unit.PERCENTAGE);
    panel.setContent(splitPanel);

    Tree tree = new Tree("Menu");
    splitPanel.setFirstComponent(tree);

    Label home = new Label("Home");

    Map<String, Component> map = new HashMap<>();
    map.put("Teachers", teachersView);
    map.put("Home", home);

    map.forEach((k, v) -> tree.addItem(k));

    tree.addItemClickListener(event -> {

      Component view = map.get(event.getItemId());
      if (view instanceof Reloadable) {
        ((Reloadable) view).reload();
      }
      splitPanel.setSecondComponent(view);
    });

    splitPanel.setSecondComponent(home);
    setContent(panel);
  } // init()
}

注意 tree.addItemClickListener ,我必须检查每个组件是否实现了 Reloadable ,如果为真,则调用它。

有效。但我不知道它是否是 standard 实现这一目标的方式?我想这应该是一个普遍的场景,应该有类似内置接口的组件来实现,比如onRender之类的(但我找不到)。我错过了什么吗?

谢谢。

首先,我将推荐您可能已经看过的关于 Spring & Vaadin 的教程,但我会在几个地方引用它,我认为这是 Vaadin 的一个很好的起点& Spring 整合。

其次,出于好奇,为什么要使用树来构建菜单?

在所提供的示例中,您似乎正在对某些视图之间的导航功能进行建模,这已经available in Vaadin, and since you're using Spring, the Vaadin spring & spring-boot extensions makes it really easy to define and navigate between your views. Then you can define some specific behaviour for each view in their own enter() method. I've used the Vaadin dashboard demo作为以下更改的灵感:

@SpringView(name = TeachersView.NAME)
public class TeachersView extends VerticalLayout implements View {
    public static final String NAME = "Teachers";

    private Label title = new Label("Teachers view");

    @PostConstruct
    void init() {
        addComponent(title);
    }

    @Override
    public void enter(ViewChangeListener.ViewChangeEvent event) {
        // recreate or reload stuff here
        title.setValue("Teachers view reloaded @ " + new Date());
    }
}
@SpringView(name = HomeView.NAME)
public class HomeView extends VerticalLayout implements View {
    public static final String NAME = "";

    @PostConstruct
    void init() {
        addComponent(new Label("Home"));
    }

    @Override
    public void enter(ViewChangeListener.ViewChangeEvent event) {
        // meh, nothing special to do here
    }
}
public class SpringVaadinUI extends UI {

    @Autowired
    private SpringViewProvider viewProvider;

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        addStyleName(ValoTheme.UI_WITH_MENU);
        Panel panel = new Panel("Admin Panel");

        HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
        splitPanel.setSplitPosition(15, Unit.PERCENTAGE);
        panel.setContent(splitPanel);

        VerticalLayout navigationBar = new VerticalLayout();
        navigationBar.setPrimaryStyleName(ValoTheme.MENU_ROOT);
        navigationBar.addComponent(createNavigationButton("Home", FontAwesome.HOME, HomeView.NAME));
        navigationBar.addComponent(createNavigationButton("Teachers", FontAwesome.GROUP, TeachersView.NAME));
        splitPanel.setFirstComponent(navigationBar);

        CssLayout navigationDisplay = new CssLayout();
        splitPanel.setSecondComponent(navigationDisplay);

        Navigator navigator = new Navigator(this, navigationDisplay);
        navigator.addProvider(viewProvider);

        setContent(panel);
    }

    private Button createNavigationButton(String caption, FontAwesome icon, final String viewName) {
        Button button = new Button(caption, icon);
        button.setPrimaryStyleName(ValoTheme.MENU_ITEM);
        button.addStyleName(ValoTheme.BUTTON_SMALL);
        button.addStyleName(ValoTheme.BUTTON_BORDERLESS);
        button.addClickListener(event -> getUI().getNavigator().navigateTo(viewName));
        return button;
    }
}

结果类似于:

如果由于某种原因您不能或不想使用导航器,那么您的解决方案看起来不错。尽管如此,无论您选择使用哪种解决方案,您都应该知道 by default Spring creates singletons. Except a few such as the UI, you should probably change your components to prototypes 这样您每次都会获得一个新实例。否则,您的所有用户在访问应用程序时都会获得相同的实例,我认为您不希望发生这种情况。