TokenField - new component

Hi all!

My TokenField component is nearing 1.0, so I thought it’s time for some feedback - bring it on!

TokenField is technically a multiselect ComboBox that can be configured extensively. But I think it’s best not to try to explain too much with words, see the screenshots, video, or online demo instead:


TokenField add-on



YouTube screencapture
(crappy, crashed when I tried HD)

NEW video


Online demo


Google code project, SVN


Edit: updated urls, removed old screenshots, see post below for upates.

Not a lot of feedback here, but TokenField still reached 1.0, with a lot of subtle and not-so-subtle improvements.
It’s re-styled, CssLayout is the default and works very well, focus stays in place, and backspace/del deletes tokens - those are the main things I can think of right now.

Feature list:

  • 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”

Check out the new & improved
YouTube video
for an overview.

In the
online demo
, the black example has almost everything customized - confirm dialogs etc. Play with it! (Note: it’s trying to showoff the features, not be the most usable email entry there is…)

Feedback still wanted…

There is a
google code page with SVN and issue tracker

Best Regards,
Marc
11220.png
11221.png

Hi,
Some methods returns the tokens selected?

Thanks in advance!

Hi,

TokenField implements the Field interface (which means it’s also a Property), so you get its value by using getValue(). You can also use a ValueChangeListener. The value returned is a Set:

TokenField f = new TokenField("Add tags");
p.addComponent(f);
f.addListener(new ValueChangeListener() {                    
   public void valueChange(ValueChangeEvent event) {
      Set tokens = (Set)event.getProperty().getValue();
      getWindow().showNotification("Tokens: "+ tokens);
   }
});

Hope this helps!
…I should really add this snippet to the examples - thanks for making me notice that :slight_smile:

Best Regards,
Marc

hello marc

your component is excellent , how ever I have a small problem: how to add a new tag if I populate the control from the database

here is my code :



		HorizontalLayout tokenLayout= new HorizontalLayout();
		tokenLayout.setSpacing(true);
		
		tags = new TokenField("Tags", tokenLayout);
		
		
		IndexedContainer tokenContainer= new IndexedContainer();
		tokenContainer.addContainerProperty("dpc_name", String.class, "");
		
		String sql="SELECT\r\n" + 
				"  dpt_tag.dpc_handle,  \r\n" + 
				"  dpt_tag.dpc_name \r\n" + 
				"  \r\n" + 
				"FROM \r\n" + 
				"  public.dpt_tag\r\n" + 
				"ORDER BY\r\n" + 
				"  dpt_tag.dpc_name ASC"; 
				
		
		try {
			java.sql.Connection conn=Dproject_webApplication.getDs().getConnection();
			Statement stmt=conn.createStatement();
			ResultSet rs= stmt.executeQuery(sql);
			
			while (rs.next()){
			Item item= tokenContainer.addItem(rs.getString("dpc_handle"));
			item.getItemProperty("dpc_name").setValue(rs.getString("dpc_name"));
			}
			stmt.close();
			rs.close();
			conn.close();
			}
		
		catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		tags.setStyleName(TokenField.STYLE_TOKENTEXTFIELD); 
        tags.setWidth("100%");
        tags.setInputWidth("100%"); 
        tags.setContainerDataSource(tokenContainer); 
        tags.setFilteringMode(ComboBox.FILTERINGMODE_CONTAINS); 
        tags.setTokenCaptionPropertyId("dpc_name"); 
        tags.setNewTokensAllowed(true);
        tags.setRememberNewTokens(true);
        
	
	

Which method should I override and how ? (I am fine witht sql or jdbc , my problemi is which methods to call or override?)

This add-on is just what I was looking for!
Any ideas about how I can use it in a form with a FieldFactory? I want to get the tags as a List preferably of the same type as the BeanItemContainer.

Thanks
Nabeel

Hi Marc,

I really love this component, it works superb!

One feature I am currently missing is to add an icon to a label, e.g., a flag next to the name of a Country. Is there a way to customize the current component to include this feature, or does the component itself needs to be extended with the ComboBox’s itemIcon feature?

Best regards,
Johannes

Hello Nabeel,

did you manage to do what you described?

I am currently trying the same.
I always get an ConversionException because PropertyFormatter tries to cast from HashSet containing the Tags to a String, which is abviously not possible.

I also tried to subclass PropertyFormatter and implemented parse/format methods which do this convertion.

It seems to me that Vaadin-Formfields are generally not really compatible with lists?!

Would like to hear about your further experiences.

br,
Tim

I don’t know about this add-on, but as a general comment: forms and fields certainly are compatible with various data structures, and e.g. using a list in multi-select mode will use a Set as its value. However, Property
Formatter
only handles conversions between a String and some other representation.

Support for easy conversion of values between a field and the underlying Property should be improved in the future.

The
CustomField
add-on (including the class FieldWrapper in it, which might be of use for you) does support other conversions and provides a PropertyConverter interface.

Hello,

for anyone who might be interested, it is working for me now, see:

my other Post

Great component, just wondering if there is a way to disable the input box?

I’ll have to look at the source code of the demo I guess

Hi it should be possible to hide it through css like this:

.mytokenfield .v-filterselect {
visibility: hidden;
}

Hi great addon, Is it possible to add an option to set the description of the tokens? I need to set it to something else than “Click to remove”

Hello Marc

I have trouble with TokenField. Would you like to help me?

TokenField displays wrong item caption?

Below is how i used it in a form FieldFactory… Hope this helps!

        else if ("roles".equals(pid)) {
            // We want this to be vertical
            VerticalLayout layout = new VerticalLayout();
            layout.setSpacing(true);
            
        	rolesField = new TokenField(SystemMsg.USER_ROLES.get(), layout) {

				private static final long serialVersionUID = -6343481987196066377L;

				// Display the dialog if not in 'role list', otherwise just add
	            @SuppressWarnings("unchecked")
				protected void onTokenInput(Object tokenId) {
	            	Set<Object> set = (Set<Object>) getValue();
	            	Role role = new Role(tokenId.toString());

	            	if (set != null && set.contains(role)) {
	                    // duplicate role
	                    getWindow().showNotification(
	                            getTokenCaption(tokenId) + " is already added");
	                } else {
	                    if (!cb.containsId(tokenId)) {
	                        // don't add directly,
	                        // show custom "add to role" dialog		                    	
	                        getWindow().addWindow(new EditRoleWindow(role, this));
	                    } else {
	                    	
	                    	
	                        // it's in the role list, just add
	                        addToken(tokenId);
	                    }
	                }
	            }
	            
	            // show confirm dialog
	            protected void onTokenClick(final Object tokenId) {
	                getWindow().addWindow(new RemoveWindow((Role) tokenId, this));
	            }

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

	            protected void configureTokenButton(Object tokenId, Button button) {
	                super.configureTokenButton(tokenId, button);
	                // custom caption
	                // button.setCaption(getTokenCaption(tokenId));
	                button.setCaption(((Role) tokenId).getName());
	                // width
	                button.setWidth("100%");

	                if (!cb.containsId(tokenId)) {
	                    // it's not in the role; style
	                    button.addStyleName(TokenField.STYLE_BUTTON_EMPHAZISED);
	                }
	            }
	            
	            public void commit() {
	            	Set<Object> set = (Set<Object>) getValue();
	            	List<Role> roles = new ArrayList<Role>();
	            	Iterator it = set.iterator();
	            	while(it.hasNext())
	            		roles.add((Role)it.next());
	            	user.setRoles(roles);
	            	FacadeFactory.getFacade().store(user);
	            	getWindow().showNotification("Information about this user saved.");
	            }
	                        
	        };

	        
	        // This would turn on the "fake" look:
	        rolesField.setStyleName(TokenField.STYLE_TOKENFIELD);
	        rolesField.setWidth("200px");
	        rolesField.setInputWidth("200px");
	        rolesField.setContainerDataSource(roleContainer); // container for role input combobox
	        rolesField.setRequired(true);
	        rolesField.setImmediate(true);
	        rolesField.setFilteringMode(ComboBox.FILTERINGMODE_CONTAINS); // suggest
	        rolesField.setTokenCaptionPropertyId("name"); // use name in input
	        rolesField.setTokenCaptionMode(ComboBox.ITEM_CAPTION_MODE_PROPERTY);
	        rolesField.setInputPrompt("Enter the role name");
	        rolesField.setRememberNewTokens(false); // we'll do this via the dialog

	        // Display the current roles
	        List<Role> roles = user.getRoles();
	        for (Role role : roles) {
	        	rolesField.addToken(role);
	        }
	        
	        return rolesField;
        		
        }
        	        
        return field;
    }

Hi,

I want to ask how can I get value of TokenField and change it to string, because I want to set value of TokenField in TextArea. I know that there are some methods like getValue() and toString(), but using getValue() I have nothing.

Really great component, and easy to use.
I only found it confusing when i wanted to get selected tokens. I used getTokenIds() instead of getValue().
I think it would be really usefull to add getTokens() method to TokenField class.

Could you please move this file to some special subfolder like tokenfield

because there are collisions with other plugins like wizards-for-vaadin.

Compiled code contains either one styles.css or another not both.
http://code.google.com/p/tokenfield/issues/detail?id=9&thanks=9&ts=1350480504
Thank you
PS good job

When using a fixed width tokenField and cssLayout, it would be nice the input box to expand to the remaining space, instead of leaving it unused. I have tried to do the trick with CSS styling, with no luck. Do you think this can be achieved this using CSS, or should this be achieved via component source modification?

Hi, Marc-

This control is exactly what I want to use, thank you! One small issue under Vaadin 7 to do with all the refactoring going on… the 7 version needs a re-compile against a corrected import since using the control now yields a compile error:

“The type com.vaadin.terminal.Sizeable$Unit cannot be resolved. It is indirectly referenced from required .class files”

I’d fix myself, but I’m not really confident messing about with plug-ins yet.

Best,
Elliott