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 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.
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.
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?
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.