fredddyTech
fredddyTech

Reputation: 21

Vaadin7 dynamically populate combobox from Rest API

I would like to dynamically populate a combobox from Vaadin7 using a rest API. The API returns a lot of values if not filtered.

This is the basic idea I have. I know that the version of Vaadin contains better ways to bind data but I need to use this is a new-old legacy system.

My main problem is that the event does not fire when typing new values and also I am not sure how to get the current value in the combobox so I can send it as a filter to the API

Here is the code

@SpringUI
public class MainView extends UI {

  @Autowired
  private NameService nameService;

  private ComboBox comboBox;

  @Override
  protected void init(VaadinRequest request) {
    VerticalLayout verticalLayout = new VerticalLayout();

    comboBox = new ComboBox();
    comboBox.setContainerDataSource(getDataSource());
    comboBox.setItemCaptionMode(ItemCaptionMode.PROPERTY);
    comboBox.setItemCaptionPropertyId("name");
    comboBox.setImmediate(true);

    comboBox.addValueChangeListener(event -> {
          // When I manually enter a property I want to go to the server
          // Rest API and get a new List of names
          String filter = event.getProperty().toString();
          comboBox.setContainerDataSource(getDataSourceWithFilter(filter));
        }
    );

    verticalLayout.addComponent(comboBox);
    setContent(verticalLayout);
  }

  // Get 20 names with a filter
  private BeanContainer<String, Name> getDataSourceWithFilter(String filter) {
    BeanContainer<String, Name> container = new BeanContainer<>(Name.class);

    container.setBeanIdResolver(Name::getId);
    // this list comes from a webservice API
    List<Name> names = nameService.fetch(filter, 0, 20).collect(Collectors.toList());
    container.addAll(names);

    return container;
  }

  // Get 20 (default) names without filtering
  private BeanContainer<String, Name> getDataSource() {
    return getDataSourceWithFilter("");
  }
}

Upvotes: 1

Views: 143

Answers (1)

fredddyTech
fredddyTech

Reputation: 21

I have found the solution by creating my own Combobox and overriding the changeVariables method. Thanks to all of you who pointed me to the right direction.

So it looks like this:

public class FilteringComboBox extends ComboBox {

  @Override
  public void changeVariables(Object source, Map<String, Object> variables) {
    if (variables.containsKey("filter")) {
      final String text = variables.get("filter").toString();
      fireEvent(new TextChangeEvent(this) {

        @Override
        public String getText() {
          return text;
        }

        @Override
        public int getCursorPosition() {
          return text.length();
        }
      });
    }
    super.changeVariables(source, variables);
  }

  public void addTextChangeListener(TextChangeListener listener) {
    addListener(TextChangeListener.EVENT_ID, TextChangeEvent.class, listener, TextChangeListener.EVENT_METHOD);
  }

  public void removeTextChangeListener(TextChangeListener listener) {
    removeListener(TextChangeListener.EVENT_ID, TextChangeEvent.class, listener);
  }
}

And then I used it in the main UI as follows:

@SpringUI
public class MainView extends UI {

  @Autowired
  private NameService nameService;

  private FilteringComboBox comboBox;

  @Override
  protected void init(VaadinRequest request) {
    VerticalLayout verticalLayout = new VerticalLayout();

    comboBox = new FilteringComboBox();
    comboBox.setContainerDataSource(getDataSource(""));
    comboBox.setItemCaptionMode(ItemCaptionMode.PROPERTY);
    comboBox.setItemCaptionPropertyId("name");
    comboBox.setImmediate(true);

    comboBox.addTextChangeListener(event -> {
          String filter = event.getText();
          comboBox.setContainerDataSource(getDataSource(filter));
        }
    );

    verticalLayout.addComponent(comboBox);
    setContent(verticalLayout);
  }

  private BeanContainer<String, Name> getDataSource(String filter) {
    BeanContainer<String, Name> container = new BeanContainer<>(Name.class);

    container.setBeanIdResolver(Name::getId);

    List<Name> names = nameService.fetch(filter, 0, 20).collect(Collectors.toList());
    container.addAll(names);

    return container;
  }
}

Upvotes: 1

Related Questions