In Vaadin 7.x, I have a ComboBox setup using a JPAContainer. It's setup like:
mycombo.setItemCaptionPropertyId("name");
Works great. However, the entity that is mapped to that combo uses a CHAR(50) column for the name and this cannot be changed. Everything works fine, but it's annoying to see all of those spaces when I copy/paste, for example, from the combo to some other text input.
Naturally, I .trim() the combo value when I pass it in programmatically but I would like to always trim the display automatically.
Would I do this by creating a custom converter?
Thanks
Due to a lack of time I can't find anything better than the following:
public class MyVaadinUI extends UI {
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = MyVaadinUI.class, widgetset = "com.gabrielruiu.vaadin.AppWidgetSet")
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
ComboBox comboBox = new ComboBox("my combobox", Arrays.asList(" Killer croc ", " Joker", "Penguin "));
comboBox.setConverter(new Converter<Object, String>() {
#Override
public String convertToModel(Object value, Class<? extends String> targetType, Locale locale) throws ConversionException {
if (value != null) {
return value.toString();
}
return null;
}
#Override
public Object convertToPresentation(String value, Class<?> targetType, Locale locale) throws ConversionException {
if (value != null) {
return value.trim();
}
return null;
}
#Override
public Class<String> getModelType() {
return String.class;
}
#Override
public Class<Object> getPresentationType() {
return Object.class;
}
});
layout.addComponent(comboBox);
}
}
The problem is that, while testing it, the value in the combobox disappears after I select it. If you can find a solution then that's great but this is what I could scrounge up for the moment.
Related
I have a problem with the component AjaxFormComponentUpdatingBehavior.
When I add this component to the main page, springs security enables authorization, as a protected page. When I remove this component from the page, everything gets good.
My UNSECURED_RESOURCE_LIST "/resources/", "/assets/", "/css/",
"/fonts/", "/webjars/", "/img/", "/js/**".
Here is my wicket page, as you can see when I add AjaxFormComponentUpdatingBehavior component, spring security enables authorization.
public class FrontPage extends WebPage {
private List<Language> languages = Arrays.asList(Language.RUSSIAN,
Language.ENGLISH);
private Language language = Language.RUSSIAN;
private DropDownChoice languagesDD;
public FrontPage() {
languagesDD = new DropDownChoice<String>("languages", new
PropertyModel(FrontPage.this, "language"), new
PropertyModel(FrontPage.this, "languages"), new ChoiceRenderer() {
#Override
public Object getDisplayValue(Object object) {
Language language = (Language) object;
if (Language.RUSSIAN.equals(language)) {
return getString("Language.RUSSIAN");
} else if (Language.ENGLISH.equals(language)) {
return getString("Language.ENGLISH");
}
return super.getDisplayValue(object);
}
});
add(languagesDD);
languagesDD.add(new AjaxFormComponentUpdatingBehavior("change") {
#Override
protected void onUpdate(AjaxRequestTarget target) {
//iWebSession().setLocaleString(language.getCode());
//setResponsePage(FrontPage.class);
}
});
}
}
When i comment this component. Everything become OK.
I guess that maybe the AjaxFormComponentUpdatingBehavior component is trying to use the resources that are in the protected area.
I added the resources to the non-security list.
So I cannot understand why spring enables authorization when I add AjaxFormComponentUpdatingBehavior component.
public class FrontPage extends WebPage {
private List<Language> languages = Arrays.asList(Language.RUSSIAN,
Language.ENGLISH);
private Language language = Language.RUSSIAN;
private DropDownChoice languagesDD;
public FrontPage() {
languagesDD = new DropDownChoice<String>("languages", new PropertyModel(FrontPage.this, "language"), new PropertyModel(FrontPage.this, "languages"), new ChoiceRenderer() {
#Override
public Object getDisplayValue(Object object) {
Language language = (Language) object;
if (Language.RUSSIAN.equals(language)) {
return getString("Language.RUSSIAN");
} else if (Language.ENGLISH.equals(language)) {
return getString("Language.ENGLISH");
}
return super.getDisplayValue(object);
}
});
add(languagesDD);
// languagesDD.add(new AjaxFormComponentUpdatingBehavior("change") {
// #Override
// protected void onUpdate(AjaxRequestTarget target) {
// //iWebSession().setLocaleString(language.getCode());
// //setResponsePage(FrontPage.class);
// }
// });
}
}
OK, I found a solution.
Just add "/wicket/**" to your unsecured resource list.
Wicket use of models can be cumbersome. For a stateful page to properly render an object, you need to use lots of boiler-plate code, overriding classes to properly get the visibility status, etc... A simple example:
private IModel<FooBar> fooBarModel;
public MyPage() {
Label lbl1 = new Label("field1",
new PropertyModel<>(fooBarModel, "field1")) {
#Override public boolean isVisible() {
return fooBarModel.getObject().someCondition();
} }
add(lbl1);
/// Etc... same for dozen of other fields
}
I'm often using a trick using a ListView to help. Same example:
public MyPage() {
add(new ListView<FooBar>("content",
new SingleListModel<FooBar>(fooBarModel)) {
#Override protected void populateItem(...) {
FooBar fooBar = item.getModelObject();
// Code here gets simpler:
Label lbl1 = new Label("field1", fooBar.getField1());
lbl1.setVisible(fooBar.someCondition());
item.add(lbl1);
// Etc...
}
});
}
With a simple utility class SingleListModel, that transform a IModel<T> to a ListModel<T>, having 1 or 0 elements, depending whether T is null or not:
public class SingleListModel<T>
extends LoadableDetachableModel<List<T>> {
private IModel<T> tModel;
public SingleListModel(IModel<T> tModel) {
this.tModel = tModel;
}
#Override
protected List<T> load() {
List<T> ret = new ArrayList<>(1);
T t = tModel.getObject();
if (t != null)
ret.add(tModel.getObject());
return ret;
}
}
The nice side-effect of this is that the whole "content" element in the markup is hidden if fooBarModel returns null; no special treatment needed.
But all this smells like a hack to me, as I use ListView in a somehow "unnatural" fashion.
Is there a cleaner way to get the same result? A standard wicket framework?
You should use Behavior instead to avoid such duplications.
public class MyBehavior extends Behavior {
private final MyModel model;
public MyBehavior(MyModel model) {this.model = model;}
#Override public void onConfigure(Component c) {
if (model.someCondition()) {
component.setVisible(false);
}
}
}
Usage:
MyBehavior b = new MyBehavior(modelInstance);
component1.add(b);
component2.add(b);
// dozen more
Label lbl1 = new Label("field1",
new PropertyModel<>(fooBarModel, "field1")) {
#Override public boolean isVisible() {
return fooBarModel.getObject().someCondition();
} }
add(lbl1);
with little refactoring it can be converted into
add(new FLabel("id","text")
.setVisibilityFunction(()->model.getObject().isVisible()))
);
the FLabel class:
public class FLabel extends Label implements IComponentWithVisibilityFunction<FLabel> {
private SerializableBooleanSupplier visibilityFunction;
public FLabel(String id) {
super(id);
}
public FLabel(String id, Serializable label) {
super(id, label);
}
public FLabel(String id, IModel<?> model) {
super(id, model);
}
#Override
public FLabel setVisibilityFunction(SerializableBooleanSupplier visibilityFunction) {
this.visibilityFunction = visibilityFunction;
return this;
}
#Override
protected void onConfigure() {
if (visibilityFunction != null) {
setVisible(visibilityFunction.getAsBoolean());
}
}
}
public interface IComponentWithVisibilityFunction<T> {
T setVisibilityFunction(SerializableBooleanSupplier visibilityFunction);
}
Moreover you can put supplier into constructor:
add(new FLabel("id","text", ()->model.getObject().isVisible()));
I'm trying to bind a custom validator to a TextField BeanFieldGroup in vaadin.
Although my validator only returns true, the validation fails each time. What am I doing wrong here?
class Order {
private BigDecimal price;
}
class MyView {
public void init() {
TextField priceField = new TextField("Enter Price");
BeanFieldGroup<Order> binder = new BeanFieldGroup<Order>(Order.class);
binder.bind(priceField, "price");
priceField.addValidator(new AbstractValidator<String>("Price invalid") {
#Override
protected boolean isValidValue(String value) {
return true;
}
#Override
public Class<String> getType() {
return String.class;
}
});
Button saveButton = new SaveButton("Save", new ClickListener() {
try {
editor.commit();
} catch (CommitException e) {
e.printStackTrace();
showValidationErrors(true);
}
});
}
}
Result: whenever I commit the binder, I'm getting the error com.vaadin.data.fieldgroup.FieldGroup$CommitException: Commit failed Price invalid.
So, although I always return true by the validator, it's not working. Did I miss anything?
To answer my own question: the validator has to be of the same type of the binding property. Changing to AbstractValidator<BigDecimal> solves the error.
I have a bean, which I attached to the form using Model and it works fine. Also I have a field in bean like Map<String, javax.mail.Address>. How can I bind this field with form by model considering that every map entry should be like Label: TextField?
Thanks in advance.
Maybe something like:
ListView<String> textAreasListView = new ListView<String>("someid", bean.map.keySet()) {
#Override
protected void populateItem(final ListItem<String> itemLang) {
itemLang.add(new Label("label", itemLang.getModelObject()));
Model<String> textModel = new Model<String>() {
#Override
public String getObject() {
return bean.map.get(itemLang.getModelObject()).toString;
}
#Override
public void setObject(String object) {
bean.map.put(itemLang.getModelObject(), new Address(object));
}
};
itemLang.add(new TextField<String>("email", textModel));
}
};
and add this to your form.
A custom converter for Address & String would be an additional improvement.
I think, you could bind it in such a way:
TextField<Address> textField = new TextField<Address>("address",
beanModel.<Address> bind("addressMap[addressKey]"), Address.class) {
#SuppressWarnings("unchecked")
#Override
public <C> IConverter<C> getConverter(Class<C> type) {
if (Address.class.isAssignableFrom(type)) {
return (IConverter<C>) new AddressConverter();
} else {
return super.getConverter(type);
}
}
};
form.add(textField);
Here "addressMap" is name of the map field, "addressKey" is the key of the address value in the map.
The listing for AddressConverter class:
public class AddressConverter implements IConverter<Address> {
public Address convertToObject(String string, Locale locale) {
try {
return new InternetAddress(string);
} catch (AddressException e) {
return null;
}
}
public String convertToString(Address address, Locale locale) {
return address.toString();
}
}
Of course, converter for Address class may be attached globally with the ConverterLocator. Just add the following method to your Application class:
#Override
protected IConverterLocator newConverterLocator() {
ConverterLocator locator = new ConverterLocator();
locator.set(Address.class, new AddressConverter());
return locator;
}
And then you don't need to override getConverter() method on the all text fields.
I have an editorpane which displays editable information which changes based on what the user has selected in a list. So, for implementing my undo/redo feature I must first re-select the item that the user had selected when making their edits prior to undoing/redoing them. However, I am finding it difficult to add functionality to default document events because when I attempt to extend that class I get the "No enclosing instance" error.
below is my undomanager code. I know there is no setSelection and getCurrentSelection for JEditorPane but just pretend I am using an extended version with those features to determine what item the user has selected.
public class MyUndoManager extends UndoManager {
private JEditorPane editor;
public MyUndoManager() {
super();
}
public void setEditor(JEditorPane editor) {
this.editor = editor;
}
#Override
public synchronized boolean addEdit(UndoableEdit anEdit) {
if (anEdit instanceof javax.swing.text.AbstractDocument.DefaultDocumentEvent) {
try {
MyDocumentEvent evt = ((MyDocumentEvent) anEdit);
evt.setCallbackObj(editor.getCurrentSelection());
return super.addEdit(anEdit);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} else {
return super.addEdit(anEdit);
}
}
private class MyDocumentEvent extends javax.swing.text.AbstractDocument.DefaultDocumentEvent {
private Object callbackObj;
public void setCallbackObj(Object o) {
this.callbackObj = o;
}
#Override
public void undo() throws CannotUndoException {
editor.setSelection(o);
super.undo();
}
#Override
public void redo() throws CannotRedoException {
editor.setSelection(o);
super.redo();
}
}
}
It could be accessible by your code if DefaultDocumentEvent were static field of AbstractDocument.