Убедитесь, что Vaadin загружает последние данные при нажатии на добавленные компоненты (такие как TabSheet или Tree )

Когда я добавляю компоненты в компонент Vaadin (например, TabSheet или Tree), добавленные компоненты кэшируются. Когда пользователь щелкает вкладку (или узлы дерева), если она содержит данные базы данных, она показывает устаревшие данные, не отражающие последнее состояние базы данных.

Интересно, есть ли способ обеспечить загрузку последних данных?

Я решаю проблему, определяя свой собственный интерфейс:

public interface Reloadable {
  void reload();
}

И каждый компонент реализует этот перезагружаемый интерфейс, например:

@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:

@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 , если true , вызвать его.

Оно работает . Но я не знаю, является ли это стандартным способом достижения этого? Я думаю, что это должен быть общий сценарий, должно быть что-то вроде встроенного интерфейса для реализации компонентов, например, onRender (но я не могу его найти). Я ничего не пропустил?

Спасибо.


person smallufo    schedule 10.03.2016    source источник
comment
ваше решение выглядит нормально, особенно если оно работает. Мы можем думать о методе attach() в интерфейсе компонента, но attach() будет вызываться только один раз, когда компонент будет добавлен в контейнер.   -  person Dragan Radevic    schedule 10.03.2016


Ответы (1)


Прежде всего, я собираюсь предложить это руководство на Spring & Vaadin, который вы, возможно, уже видели, но я буду ссылаться на него в нескольких местах, и я думаю, что это хорошая отправная точка для интеграции Vaadin и Spring.

Во-вторых, из любопытства, почему вы используете дерево для построения меню?

В приведенном примере вы, похоже, моделируете переход между некоторыми представлениями, который уже доступно в Vaadin, и поскольку вы используете Spring, Vaadin spring и spring-boot действительно упрощают определять представления и перемещаться между ними. Затем вы можете определить определенное поведение для каждого представления в их собственном методе enter(). Я использовал демонстрацию панели управления Vaadin в качестве вдохновения для следующих изменений:

@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;
    }
}

Результат аналогичен: Демо

Если по каким-то причинам вы не можете или не хотите пользоваться навигатором, то ваше решение выглядит нормально. Тем не менее, какое бы решение вы ни выбрали, вы должны знать, что по умолчанию Spring создает одиночки. За исключением некоторых, таких как пользовательский интерфейс, вам, вероятно, следует изменить свои компоненты на prototypes, поэтому каждый раз вы будете получать новый экземпляр. В противном случае все ваши пользователи будут получать одни и те же экземпляры при доступе к приложению, чего я не думаю, что вы хотите.

person Morfic    schedule 10.03.2016
comment
Спасибо, ваше решение именно то, что я хочу. Я новичок в vaadin уже 3 дня. Не нашел, что такое Navigator на самом деле (теперь я знаю). Кстати, ваши два представления являются статическим классом, что должно быть неправильным. IntelliJ сообщает о @Autowired private SpringViewProvider viewProvider; no beans of SpringViewProvider type found, но он работает во время выполнения. - person smallufo; 10.03.2016
comment
@smallufo Вы абсолютно правы, извините, я скоро обновлю. Изначально это были внутренние классы, и мне пришлось сделать их статическими, потому что иначе Spring не смог бы их создать. Что касается SpringViewProvider, это может иметь какое-то отношение к версии IJ. Я знаю, что они добавили поддержку загрузки Spring, начиная с последней версии, возможно, 14?! В любом случае, рад, что смог помочь :-) - person Morfic; 10.03.2016
comment
Кстати, @smallufo, если вы только начинаете работать с Vaadin, вы можете взглянуть на раздел Framework в документации или Книга Vaadin. Ваше здоровье - person Morfic; 10.03.2016