Search code examples
vaadinvaadin-flowvaadin14

Passing values between classes in Vaadin


This might be more of a Java question, but how would you access values (say from a textfield) of a given view/class from a different class? For example if there was a TextField t1 that is in the MainView, and I wanted to get its current value for a computation in a different class. And is there a more Vaadin-specific approach here?


Solution

  • That can depend on the use case specifically. Since you mentioned a TextField value I assume the value is not yet stored in the DB, it's just on the UI yet -> I rule out singleton spring services.

    A few ideas:

    1. If the MainView and the different class are nested components and it's viable and not really complicated across a lot of classes... then probably passing it down the way when creating the sub-component. This is a naive solution - it can get pretty messy.

      MainView() {
          var t1 = new TextField();
          var d = new Different(t1);
      }
      
    2. Fire and listen to Vaadin Component events. If you want really loose coupling, the most universal would be to use the UI instance as the event bus.

      // listen in different class
      ComponentUtil.addListener(attachEvent.getUI(), CloseMenuEvent.class, e -> closeMenu());
      // fire change in MainView
      ComponentUtil.fireEvent(ui, new CloseMenuEvent(ui))
      
    3. A more specific version of number 2. is to pass the ValueChangeListener of the MainView's t1 to the different class.

      MainView() {
          var t1 = new TextField();
          var d = new DifferentClass();
          t1.addValueChangeListener(d::t1Changed)
          add(t1, d);
      }
      
    4. Extract the common field to a third party, to a third class. Use a @UIScoped spring bean (@SpringComponent, @Service, ...) that will hold that field, and inject it to both MainView and the different class.

      @Route
      public class MainView extends VerticalLayout {
          public MainView(Model m, Different d) {
              add(m.t1, d);
          }
      }
      
      @Scope(SCOPE_PROTOTYPE)
      public class Different extends Component {
          public Different(Model m) {
              // something with m.t1
          }
      }
      
      @UIScoped
      public class Model {
          public final TextField t1 = new TextField(); // TODO use getter
      }
      

      You could change the 4th approach by keeping String in Model and having a value change listener that updates it.