Directory

← Back

TokenField

A field for selecting multiple tokens/tags; a.k.a multi-select ComboBox

Author

Rating

Popularity

<100

A field that allows the user to select multiple 'tokens' using a ComboBox (which by default actually looks like a TextField w/ suggestions).

The field is very configurable, as can be seen in the demo.

Features include:

  • tokens can be inserted before/after input (over/under/etc depending on layout)
  • backspace/delete removes last token (customizeable)
  • layout can be changed
  • suggestions from container
  • auto add new to container
  • disallow tokens not in container
  • custom action on add (+ detect if token is in container)
  • custom configuring of the token button (style, caption, etc)
  • custom action on remove
  • built in style for either Token or ComboBox input look
  • built in styles for buttons, default and "emphasize"

Note that Vaadin 6.3.3 fixes some bugs in the ComboBox, which TokenField uses.

Sample code

                /*
                 * This is the most basic use case using all defaults; it's
                 * empty to begin with, the user can enter new tokens, 
                 * which are automatically added to the container
                 */


                TokenField f = new TokenField("Add tags");
                addComponent(f);

                TokenField f = new TokenField(lo) {
                    protected void onTokenInput(Object tokenId) {
                        // default adds the token
                    }

                    protected void onTokenClicked(final Object tokenId) {
                        // default removes w/o dialog
                    }

                    protected void configureTokenButton(Object tokenId,
                            Button button) {
                        // default sets  sets style & caption
                    }
                };
                f.setStyleName(TokenField.STYLE_TOKENFIELD); // remove fake textfield look
                f.setWidth("100%"); // width...
                f.setInputWidth("100%"); // and input width separately
                f.setContainerDataSource(tokens); // 'address book'
                f.setFilteringMode(ComboBox.FILTERINGMODE_CONTAINS); // suggest
                f.setTokenCaptionPropertyId("name"); // use container item property "name" in input
                f.setInputPrompt("Enter contact name or new email address");
                f.setRememberNewTokens(false); // we can opt to do this ourselves
{
                /*
                 * In this example, most features are exercised. A container
                 * with generated contacts is used. The input has filtering
                 * (a.k.a suggestions) enabled, and the added token button is
                 * configured so that it is in the standard "Name <email>"
                 * -format. New contacts can be added to the container ('address
                 * book'), or added as-is (in which case it's styled
                 * differently).
                 */

                Panel p = new Panel("Full featured example");
                p.getContent().setStyleName("black");
                addComponent(p);

                // generate container
                Container tokens = generateTestContainer();

                // we want this to be vertical
                VerticalLayout lo = new VerticalLayout();
                lo.setSpacing(true);

                final TokenField f = new TokenField(lo) {

                    private static final long serialVersionUID = 5530375996928514871L;

                    // dialog if not in 'address book', otherwise just add
                    protected void onTokenInput(Object tokenId) {
                        Set<Object> set = (Set<Object>) getValue();
                        Contact c = new Contact("", tokenId.toString());
                        if (set != null && set.contains(c)) {
                            // duplicate
                            getWindow().showNotification(
                                    getTokenCaption(tokenId)
                                            + " is already added");
                        } else {
                            if (!cb.containsId(c)) {
                                // don't add directly,
                                // show custom "add to address book" dialog
                                addWindow(new EditContactWindow(tokenId
                                        .toString(), this));
                            } else {
                                // it's in the 'address book', just add
                                addToken(tokenId);
                            }
                        }
                    }

                    // show confirm dialog
                    protected void onTokenClick(final Object tokenId) {
                        getWindow().addWindow(
                                new RemoveWindow((Contact) tokenId, this));
                    }

                    // just delete, no confirm
                    protected void onTokenDelete(Object tokenId) {
                        this.removeToken(tokenId);
                    }

                    // custom caption + style if not in 'address book'
                    protected void configureTokenButton(Object tokenId,
                            Button button) {
                        super.configureTokenButton(tokenId, button);
                        // custom caption
                        button.setCaption(getTokenCaption(tokenId) + " <"
                                + tokenId + ">");
                        // width
                        button.setWidth("100%");

                        if (!cb.containsId(tokenId)) {
                            // it's not in the address book; style
                            button
                                    .addStyleName(TokenField.STYLE_BUTTON_EMPHAZISED);
                        }
                    }
                };
                p.addComponent(f);
                // This would turn on the "fake tekstfield" look:
                f.setStyleName(TokenField.STYLE_TOKENFIELD);
                f.setWidth("100%");
                f.setInputWidth("100%");
                f.setContainerDataSource(tokens); // 'address book'
                f.setFilteringMode(ComboBox.FILTERINGMODE_CONTAINS); // suggest
                f.setTokenCaptionPropertyId("name"); // use name in input
                f.setInputPrompt("Enter contact name or new email address");
                f.setRememberNewTokens(false); // we'll do this via the dialog
                // Pre-add a few:
                Iterator it = f.getTokenIds().iterator();
                f.addToken(it.next());
                f.addToken(it.next());
                f.addToken(new Contact("", "thatnewguy@example.com"));
}

    /**
     * This is the window used to confirm removal
     */
    class RemoveWindow extends Window {

        private static final long serialVersionUID = -7140907025722511460L;

        RemoveWindow(final Contact c, final TokenField f) {
            super("Remove " + c.getName() + "?");

            setStyleName("black");
            setResizable(false);
            center();
            setModal(true);
            setWidth("250px");
            setClosable(false);

            // layout buttons horizontally
            HorizontalLayout hz = new HorizontalLayout();
            addComponent(hz);
            hz.setSpacing(true);
            hz.setWidth("100%");

            Button cancel = new Button("Cancel", new Button.ClickListener() {

                private static final long serialVersionUID = 7675170261217815011L;

                public void buttonClick(ClickEvent event) {
                    f.getWindow().removeWindow(getWindow());
                }
            });
            hz.addComponent(cancel);
            hz.setComponentAlignment(cancel, Alignment.MIDDLE_LEFT);

            Button remove = new Button("Remove", new Button.ClickListener() {

                private static final long serialVersionUID = 5004855711589989635L;

                public void buttonClick(ClickEvent event) {
                    f.removeToken(c);
                    f.getWindow().removeWindow(getWindow());
                }
            });
            hz.addComponent(remove);
            hz.setComponentAlignment(remove, Alignment.MIDDLE_RIGHT);

        }
    }

    /**
     * This is the window used to add new contacts to the 'address book'. It
     * does not do proper validation - you can add weird stuff.
     */
    class EditContactWindow extends Window {
        private Contact contact;

        EditContactWindow(final String t, final TokenField f) {
            super("New Contact");
            if (t.contains("@")) {
                contact = new Contact("", t);
            } else {
                contact = new Contact(t, "");
            }
            setModal(true);
            center();
            setWidth("250px");
            setStyleName("black");
            setResizable(false);

            // Just bind a Form to the Contact -pojo via BeanItem
            Form form = new Form();
            form.setItemDataSource(new BeanItem<Contact>(contact));
            form.setImmediate(true);
            addComponent(form);

            // layout buttons horizontally
            HorizontalLayout hz = new HorizontalLayout();
            addComponent(hz);
            hz.setSpacing(true);
            hz.setWidth("100%");

            Button dont = new Button("Don't add", new Button.ClickListener() {

                private static final long serialVersionUID = -1198191849568844582L;

                public void buttonClick(ClickEvent event) {
                    if (contact.getEmail() == null
                            || contact.getEmail().length() < 1) {
                        contact.setEmail(contact.getName());
                    }
                    f.addToken(contact);
                    f.getWindow().removeWindow(getWindow());
                }
            });
            hz.addComponent(dont);
            hz.setComponentAlignment(dont, Alignment.MIDDLE_LEFT);

            Button add = new Button("Add to contacts",
                    new Button.ClickListener() {

                        private static final long serialVersionUID = 1L;

                        public void buttonClick(ClickEvent event) {
                            if (contact.getEmail() == null
                                    || contact.getEmail().length() < 1) {
                                contact.setEmail(contact.getName());
                            }
                          

Compatibility

(Loading compatibility data...)

Was this helpful? Need more help?
Leave a comment or a question below. You can also join the chat on Discord or ask questions on StackOverflow.

Version

Updated to work with 7.0 final. Note: CSS selectors made stronger, you might have to do the same if you have styled TokenField.

Released
2013-02-14
Maturity
STABLE
License
Apache License 2.0

Compatibility

Framework
Vaadin 7.0+
Vaadin 6.0+ in 1.0
Browser
Internet Explorer
Firefox
Opera
Safari
Google Chrome
Internet Explorer
iOS Browser

TokenField - Vaadin Add-on Directory

A field for selecting multiple tokens/tags; a.k.a multi-select ComboBox TokenField - Vaadin Add-on Directory
A field that allows the user to select multiple 'tokens' using a ComboBox (which by default actually looks like a TextField w/ suggestions). The field is very configurable, as can be seen in the demo. Features include: - tokens can be inserted before/after input (over/under/etc depending on layout) - backspace/delete removes last token (customizeable) - layout can be changed - suggestions from container - auto add new to container - disallow tokens not in container - custom action on add (+ detect if token is in container) - custom configuring of the token button (style, caption, etc) - custom action on remove - built in style for either Token or ComboBox input look - built in styles for buttons, default and "emphasize" Note that Vaadin 6.3.3 fixes some bugs in the ComboBox, which TokenField uses.
Online