How to bind validators to TextFields in vaadin? - java

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.

Related

Wicket AjaxFormComponentUpdatingBehavior Spring Security

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.

Java Dynammic bean / shared between Users

I have java web application, that is being used by multiple users. By users I mean people that actually connect to application and do stuff with it.
Every user can edit a schema, that has it's ID. At the moment, multiple users can edit same schema at the same time. I want to fix that, without using a database/table.
What I tried so far:
There's an EDIT button, that users click to edit certain schema. When they click it, a method is triggered.
protected SessionLockModSchema sessionLockModSchema = new SessionLockModSchema();
protected Model model; //schema model object
public void buttonClick(ClickEvent event) {
//button logic goes here
}
I figured I might create a bean with schema ID, when this method is triggered.
protected SessionLockModSchema sessionLockModSchema = new SessionLockModSchema();
protected Model model;
public void buttonClick(ClickEvent event) {
//button logic goes here
this.sessionLockModSchema.lockSchema(model);
}
When I have this bean created, all that I need to do is check if it exists, for the next user.
protected Model model;
public void buttonClick(ClickEvent event) {
if(!this.sessionLockModSchema.isSchemaLocked(model){
//button logic goes here
this.sessionLockModSchema.lockSchema(model);
}
}
In theory this sounded good to me, edit button wouldn't trigger if there was a bean created with that schema. But bean is not created (or at least only one user can access it). Here's sessionLockModSchema class:
public class SessionLockModSchema{
ApplicationContext context;
GenericApplicationContext ctx;
public SessionLockModSchema(){
if(ctx == null){
this.ctx = new GenericApplicationContext();
}
}
public void lockSchema(Model model){
String beanName = "model-"+model.getId();
BeanDefinitionBuilder bDBuilder = BeanDefinitionBuilder .rootBeanDefinition(String.class);
bDBuilder.setScope("prototype");
this.ctx.registerBeanDefinition(beanName, bDBuilder.getBeanDefinition());
//appcontext.close();
}
public boolean isSchemaLocked(Model model){
String beanName = "model-"+model.getId();
Object objectRef = null;
try{
//ctx.refresh();
objectRef = this.ctx.getBean(beanName);
}catch(NoSuchBeanDefinitionException e){
// TODO:
}catch(IllegalStateException e){
// TODO:
}
boolean isLocked;
if(objectRef == null){
isLocked = false;
}else{
isLocked = true;
}
return isLocked;
}
}
To clarify my question, I get IllegalStateException saying that beanFactory must be refreshed, if I do however refresh (commented ctx.refresh), I get that no such bean exists. Any advices on this? What am I doing wrong?
What about something like that (I don't see why you would need spring managed beans here):
public enum SessionLockModSchema {
INSTANCE;
private final Set<String> lockedModels = new HashSet<>;
public void lockSchema(Model model){
synchronized(lockedModels) {
lockedModels.add("" + model.getId());
}
}
public void unlockSchema(Model model){
synchronized(lockedModels) {
lockedModels.remove("" + model.getId());
}
}
public void isSchemaLocked(Model model){
synchronized(lockedModels) {
return lockedModels.contains("" + model.getId());
}
}
}
usage (something like that):
public void buttonClick(ClickEvent event) {
if (SessionLockModSchema.INSTANCE.isLocked(model) {
try {
SessionLockModSchema.INSTANCE.lockSchema(model);
// do something with "model"
} finally {
SessionLockModSchema.INSTANCE.unlockSchema(model);
}
}
}

How can I .trim() the display field of a Vaadin combobox?

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.

Binding in JavaFX 2.0

Maybe I missunderstood JavaFX binding or there is a bug in SimpleStringProperty.
When I run this testcode my changed model value didn't get the new value. Test testBindingToModel fails. I thought my model should then be updated with the value of the TextField tf. But only the binding value of prop1Binding gets the value "test".
public class BindingTest {
private TextField tf;
private Model model;
private ModelBinding mb;
#Before
public void prepare() {
tf = new TextField();
model = new Model();
mb = new ModelBinding(model);
Bindings.bindBidirectional(tf.textProperty(), mb.prop1Binding);
}
#Test
public void testBindingToMB() {
tf.setText("test");
assertEquals(tf.getText(), mb.prop1Binding.get());
}
#Test
public void testBindingToModel() {
tf.setText("test");
assertEquals(tf.getText(), mb.prop1Binding.get());
assertEquals(tf.getText(), model.getProp1());
}
private static class ModelBinding {
private final StringProperty prop1Binding;
public ModelBinding(Model model) {
prop1Binding = new SimpleStringProperty(model, "prop1");
}
}
private static class Model {
private String prop1;
public String getProp1() {
return prop1;
}
public void setProp1(String prop1) {
this.prop1 = prop1;
}
}
}
Thanks for your help.
Best regards
Sebastian
EDIT:
With this class I can set the value of the model directly. I will test this class in the next days and comment on this post with my result.
public class MySimpleStringProperty extends SimpleStringProperty {
public MySimpleStringProperty(Object obj, String name) {
super(obj, name);
}
public MySimpleStringProperty(Object obj, String name, String initVal) {
super(obj, name, initVal);
}
#Override
public void set(String arg0) {
super.set(arg0);
if (this.getBean() != null) {
try {
Field f = this.getBean().getClass().getDeclaredField(this.getName());
f.setAccessible(true);
f.set(this.getBean(), arg0);
} catch (NoSuchFieldException e) {
// logging here
} catch (SecurityException e) {
// logging here
} catch (IllegalArgumentException e) {
// logging here
} catch (IllegalAccessException e) {
// logging here
}
}
}
}
This constructor doesn't attach SimpleStringProperty to a bean object unfortunately. It just says to SimpleStringProperty which bean property belongs to.
E.g., if you want to have a property in your class you should do it next way:
public static class Model {
private StringProperty prop1 =
new SimpleStringProperty(this, "prop1", "default_value");
public String getProp1() {
return prop1.get();
}
public void setProp1(String value) {
prop1.set(value);
}
public StringProperty prop1Property() {
return prop1;
}
}
Note, that there is no way to bind to your original Model class as it provides no events about setting new prop1 value. If you want to have observable model, you should use fx properties from the beginning.
Just figured out that there is provided the class JavaBeanStringProperty, which just fullfill my request.
Using this code I can directly bind the value of my bean to a StringProperty (included setting and getting of my value to / from my Bean).
binding = JavaBeanStringPropertyBuilder.create().beanClass(Model.class).bean(model).name("prop1").build();
The only problem I found is that when you change the value of the model after setting the binding, there is no update e.g. in the TextField.

Multiple TextField in Wicket Form

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.

Categories