CustomField component

As making customized Field implementations to use on forms has been unnecessarily hard, I’m publishing a little utility class Matti and I have developed.

CustomField is a CustomComponent that implements the Field interface. Therefore, its visual appearance and much of its logic can be customized freely. The implementation borrows quite a lot from AbstractField.

The current (version 0.5 beta) source code and a little example on how to use it is available from
http://code.google.com/p/customfield/
.

Henri,

Great work! This has already proven very useful to me.

Thanks,
Peter

The CustomField calls getWindow().setFocusedComponent(this); which is a package friendly method in Window. It would be nice to be able to call this method from our own code. I can’t implement a CustomField similar to this, nor can I simply copy&paste the code to my own project.

This issue has already come up before.

I have been considering opening the method to be public, but need to discuss the issue in the development team first - this would extend the public API and might constrain our choices in making focus management changes in the future. It might still happen for 6.3 if there are no objections.

With the changes for
ticket 1394
, it is now possible to implement Focusable without modifying or adding classes in the com.vaadin.ui package.

AbstractComponent now (in Vaadin 6.3) has a protected focus() method that can be called by a public focus() method of its Focusable subclasses.

More focus related changes may come in later versions.

HI, man =) How are you ?

Why do you use COPY but don’t use DELEGATION ?
Delegation it’s very cool because it is independed from AbstractField.
plur

Delegation in which sense? Forwarding all requests to a single contained (wrapped) field? Or using a hidden AbstractField to implement a part of the functionality? The latter is not really feasible, AbstractField has too many hidden/private members that would be needed, and some unnecessary complexity.

Wrapping another Field in a CustomField is just one use case, albeit a very common one. There, the contained field could be e.g. a form or another CustomField or a field deep inside a layout with other components. All the usual requests (setting and obtaining the value, validation etc.) should then by default go to the wrapped field, with the possibility to override this behavior.

I discussed this with someone else today, let’s see if one of us get around to implementing support for easy wrapping.

I 'm using a CustomField and i trying to add a validator that determines if a listselect is empty…

Mi code is :


	@SuppressWarnings("serial")
	public  class ListSelectField extends CustomField implements Container.Viewer {
	     private HorizontalLayout layout = new HorizontalLayout();
	     private ListSelect field=new ListSelect();
	     private Button button = new Button();
	     private Button button2 = new Button();
	     private VerticalLayout vl=new VerticalLayout();
	     public ListSelectField() {
     		 //Field
        	field.setSizeUndefined();
        	field.setItemCaptionPropertyId("fullname");
        	field.setRows(5);
        	field.setNewItemsAllowed(false);
        	field.setImmediate(true);
        	field.setRequired(true);
        	field.setMultiSelect(true);
        	field.addContainerProperty("fullname", String.class, null);
        	field.addContainerProperty("email", String.class, null);
        	field.addContainerProperty("smsphone", String.class, null);
    
    		 //Buttons
    		 button.setStyleName("icon-only");
    		 button.setIcon(new ThemeResource("img/book_open.png"));
    		 button.addListener(new Button.ClickListener() {
				
				@Override
				public void buttonClick(ClickEvent event) {
					// TODO Auto-generated method stub
					getWindow().addWindow(new SearchWindow());
				}
			});
    		 
    		 button2.setStyleName("icon-only");
    		 button2.setIcon(new ThemeResource("img/cross.png"));
    		 button2.addListener(new Button.ClickListener() {
				
				@Override
				public void buttonClick(ClickEvent event) {
					// TODO Auto-generated method stub
					Set <?>values=(Set<?>) field.getValue();
					for (Object v:values){
						field.removeItem(v);
					}
				}
			});
    		 
    		 //layouts
    		 layout.setSizeFull();
    		 vl.setSizeUndefined();
    		 vl.setSpacing(true);
    		 vl.setHeight(100, UNITS_PERCENTAGE);
    		 vl.addComponent(button);
    		 vl.addComponent(button2);
      		 layout.addComponent(field);
      		 layout.addComponent(vl);
      		 vl.setComponentAlignment(button2, Alignment.BOTTOM_CENTER);
      		 super.setWidth(100, UNITS_PERCENTAGE);;
      		 setWidth(100, UNITS_PERCENTAGE);;
      		 layout.setWidth(100, UNITS_PERCENTAGE);;
      		 field.setWidth(100, UNITS_PERCENTAGE);;
      		 layout.setExpandRatio(field, 1.0f);
      		 layout.setSpacing(true);
      		 setImmediate(true);
      		 setCompositionRoot(layout);
        }

	     
        @Override
        public Class<?> getType() {
            return ListSelect.class;
        }
		@Override
		public Container getContainerDataSource() {
			// TODO Auto-generated method stub
			return field.getContainerDataSource();
		}

		@Override
		public void setContainerDataSource(Container newDataSource) {
			// TODO Auto-generated method stub
				field.setContainerDataSource(newDataSource);
	

		public Collection getItemIds() {
			// TODO Auto-generated method stub
			return field.getItemIds();
		}


	}

This is the validator

 cl[code]

ass ListValidator implements Validator{
private ListSelectField f;
private String errormessage;
public ListValidator(String errormessage,ListSelectField f) {

			// TODO Auto-generated constructor stub
			this.f=f;
			this.errormessage=errormessage;
		}
		@Override
		public boolean isValid(Object value) {
			// TODO Auto-generated method stub
			 	try {
			         validate(f.getItemIds().size());
			       } catch (Validator.InvalidValueException ive) {
			            return false;
			        }
			        return true;
			
		}
		@Override
		public void validate(Object value) throws InvalidValueException {
			// TODO Auto-generated method stub
			if (value instanceof Integer) {
				if ((Integer)value==0){throw new Validator.InvalidValueException(errormessage);}
			}
		}
		
	}

[/code]

And this is the form field factory :



private Field f; 
		    public Field createField(Item item, final Object propertyId, Component uiContext) {
		        if ("to".equals(propertyId)) {
		        	f=new ListSelectField();
		        	f.addValidator(new ListValidator("Error items Empty",(ListSelectField)f));

I don’t know what i 'm doing wrong…

I do not know is something is wrong…
Could you help me?

Thanks in advance!!

Empty values are normally handled by the “required” - mechanism, not validators. Take a look at setRequired(true), and setRequiredError(String) for setting the error message.

If you do have to handle empty values in a validator, you need to override AbstractSelect.isEmpty() for your ListSelect.

Although already mentioned in a
separate thread
, the version 0.7 of CustomField finally has some support for easy wrapping of a field with support for customizing the layout, adding value conversions etc.

Uploaded Henri’s presentation on CustomField on http://www.youtube.com/watch?v=2tg2deeYMv8