Google Suggestionfield

Hi there,

i need something like the textfield in the google search. The suggestions i need to load from database/solr after key pressed in the textfield.

What is the best choice?

I am using at the moment a normal editable combobox. But there i have to set all tokens before while init the combo. But there are too many items. So i have to load them on the fly depending on the user inputs.

Regards
Uwe

Can i just overwrite the method:

protected List<?> getFilteredOptions() {

in order to load the items from the database while using the Combobox.

Hi,

In our project, we have rolled our own using a combination of a TextField, the Overlays plug-in and a normal Vaadin Table. Essentially, we’ve built a Vaadin container that does the searching, and tied the textfield to the container (so that any changes repopulate the container), and hide and show the table via the use of the Overlays component,

It is quite complex, I grant you : but the complexity is actually in the managing-the-popup.

Not really possible to share the code, I’m afraid, but that’s the way we went.

Cheers,

Charles.

Sounds very good…i won’t have the time now for creating my own component…even i would like to…

Is there no ready component for using?

The normal combobox is suitable for me…but i just have the problem, that i want to load the items from the database depending the user input…like after every keypress (minimum e.g. 3 characters).

Unfortunately, both getOptionsWithFilter(boolean) and getFilteredOptions() interact with private variables in Select. Therefore, overriding them might be tricky.

I’m not certain if you really need to override it, though, depending on the container used.
Using a suitable container (implementing Filterable) and ITEM_CAPTION_MODE_PROPERTY for the ComboBox might be sufficient for what you want to do.

Thanks Henri.

With the private members i regognized it already…

About the container i was thinking already…but how the container knows if he should update his content?

I have to trigger this somehow?

Like i was thinking to register as listerer to the combobox to be informed after a user key pressed and then update the container by myself…

something like this…

Is that the way? Which listener i use for the textfield (editable combo)

If you use a Filterable container and ITEM_CAPTION_MODE_PROPERTY (and nothing else blocks using container filtering), the container filtering should be automatically triggered when the user types something. Otherwise, filtering is performed in memory by getFilteredOptions(), which does not use the container for filtering but first fetches all items.

The mechanism might also trigger some extra fetches, though, as getOptionsWithFilter() adds and removes the filter so the state of the container after the operation is as before.

One potential problem with this: the container is also used when the popup is opened before the user types anything, fetching potentially the IDs of all items.

@Charles Anthony:

I tried this approach. with10-20 lines of code we can see a lot already. Problem seems really to manage the popup:

-how the user can change from the textfield to the popup in order to select the table (i use a listselect)?

  • is there a way to close to overlay after the user pressed ESC?

Reagrds

Hi,

We use
shortcut listeners
extensively. We’ve added shortcuts to the textfield for ARROW_UP/ARROW_DOWN (and HOME and END) to change the selection on the table/list. We’ve also added a shortcut for ESCAPE to hide the popup.

Another possibility is to simply call list.focus() on the table/list when you display the overlay. You’d want to add an “ESCAPE” shortcut as above to hide the overlay.

Here’s a small extract of some of our shortcut listeners, all added to the text field

Cheers,

Charles.

new ShortcutListener("", ShortcutAction.KeyCode.ESCAPE, new int[]{}) {
  @Override
  public void handleAction(Object sender, Object target) {
    if (areResultsVisible()) {
      hideSearchResults();
      searchField.focus();
    }
  }
},

new ShortcutListener("", ShortcutAction.KeyCode.ARROW_UP, new int[]{}) {
  @Override
  public void handleAction(Object sender, Object target) {
    selectPreviousRow();
  }
},

new ShortcutListener("", ShortcutAction.KeyCode.ARROW_DOWN, new int[]{}) {
  @Override
  public void handleAction(Object sender, Object target) {
    if (areResultsVisible()) {
      selectNextRow();
    } else {              
      showSearchResults();
    }
  }
},

new ShortcutListener("", ShortcutAction.KeyCode.HOME, new int[]{}) {
  @Override
  public void handleAction(Object sender, Object target) {
    selectFirstRow();
  }
},

new ShortcutListener("", ShortcutAction.KeyCode.END, new int[]{}) {
  @Override
  public void handleAction(Object sender, Object target) {
    selectLastRow();
  }
}

Thanks Charles…!

What works well in my code below:

  • User can type, after he stops typing i do the query and update the content of the list, also the overlay become visible
  • When the textfield still has the focus, after ESC the overlay hides
  • When the textfield still has the focus, after KEY DOWN the overlay becomes the focus

What doesn’t work well:

  • The user can navigate within the list…but when he press ESC the overlay doesn’t disapear. (just if i click outside of the overlay, and press then ESC, it dissapears).

Any ideas? What could i do better in general, what could i do better for the described problem?

THANKS in advance!


...
public class ProjectAutoCompleteField2 extends CssLayout implements KeyPressListener {
    private final IndexedContainer listSelectContainer = new IndexedContainer();
    private final CustomOverlay overlay;
    private final SuperImmediateTextField filterField;
    private final ListSelect select;
    private String lastUserInput = "";

    private final ObjectProperty<String> querySource;
    private final FilterModel model;

    /**
     * Initialisiert das Autocomplete Feld mit Vorschlägen
     */
    public ProjectAutoCompleteField2(FilterModel model) {
        this.querySource = model.getQueryString();
        this.model = model;

        // Textfield:
        filterField = new SuperImmediateTextField(querySource);
        filterField.setTextChangeEventMode(TextChangeEventMode.LAZY);
        filterField.setTextChangeTimeout(200);
        filterField.focus();
        filterField.setWidth("400px");
        filterField.addListener(this);
        addComponent(filterField);

        // Escape:
        filterField.addShortcutListener(new ShortcutListener("", ShortcutAction.KeyCode.ESCAPE, new int[] {}) {
            @Override
            public void handleAction(Object sender, Object target) {
                overlay.setVisible(false);
                filterField.focus();
            }
        });

        // Key Down:
        filterField.addShortcutListener(new ShortcutListener("", ShortcutAction.KeyCode.ARROW_DOWN, new int[] {}) {
            @Override
            public void handleAction(Object sender, Object target) {
                overlay.setVisible(true);
                select.focus();
            }
        });

        // List:
        select = new ListSelect(null, listSelectContainer);
        select.setRows(7); // perfect length in out case
        select.setNullSelectionAllowed(false); // user can not 'unselect'
        select.setImmediate(true); // send the change to the server at once
        select.setWidth("400px");

        // Overlay:
        overlay = new CustomOverlay(select, filterField);
        addComponent(overlay);
        overlay.setXOffset(10);
        overlay.setYOffset(21);
        overlay.setVisible(false);
    }

    @Override
    public void keyPressed(KeyPressEvent event) {
        String filter = querySource.getValue();
        overlay.setVisible(false);

        if (filter.length() > 2 && !lastUserInput.equals(filter)) {
            listSelectContainer.removeAllItems();

            // Load datas:
            List<String> items = model.getAutoCompleteTokens(filter);
            if (items.size() > 50) {
                items = items.subList(0, 50);
            }

            for (String s : items) {
                listSelectContainer.addItem(s);
            }
            overlay.setVisible(!items.isEmpty());
        }
        lastUserInput = filter;
    }
}

Charles, i think now i understood. In your case, the textfield never looses the focus. Thats why you have to do the navigation (end,start,down,up,…) by yourself?

Hi,

Yes - in this case, the text field retains the focus, hence the managing of UP and DOWN.

In your case, if you want to have escape dismiss the overly when the list is visible - you’ll have to bind a shortcut listener to a parent of the list. So - I think the easiest way to do this is to add a Panel as the root of the overlay, and then add the list to the panel - e.g.

Overlay
  Panel
    List

You can then add the shortcut listener for ESCAPE to the panel. (You can of course configure the panel to be invisible to the user). It might be possible to add the shortcut listener directly to the list itself - I’ve not tried it.

HTH,

Cheers,

Charles.

Charles, thanks a lot for your hints and support!

With your 2 hints i solved now my problem, or in other words i have now the component i want:

A suggestion field, based on the add-ons superimmediatetextfield and overlay. it does exactly what i want!

works well and looks well.

I have following strange problem while using hte overlay with IE.

After i select a item from the listselect wich is on the overlay i start the query and close the overlay.

the overlay becomes invisible. But when i scroll the mouse (the mouse cursor is arround the invisible overlay) the overlay/listselect suddenly becomes visible.

In Firefox this doesn’t happen.

Any ideas?

I have the same problem with an custom Overlay with the Overlays plugin. Have you found out something?

unfortunately not! but it is urgent and i need help here…