Vaadin 7 ComboBox rendering - java

So I am experiencing some odd behaviour using Vaadin 7 and the ComboBox component. Essentially, what is happening is that when it first renders the form, it appears to neither have selected the null selection nor any of the items added.
I have attempted to recreate this behaviour with the following code and this demonstrates the issue.
#Override
protected void init(VaadinRequest request) {
final FieldGroup binder;
FormLayout form = new FormLayout();
form.setMargin(true);
setSizeFull();
setContent(form);
final Label output = new Label();
form.addComponent(output);
ComboBox box = new ComboBox("My Dropdown");
final PropertysetItem fields = new PropertysetItem();
fields.addItemProperty("country", new ObjectProperty<String>(""));
binder = new FieldGroup(fields);
binder.bind(box, "country");
box.addItem("aus");
box.setItemCaption("aus", "Australia");
box.addItem("uk");
box.setItemCaption("uk", "United Kingdom");
box.setRequired(true);
box.setImmediate(true);
box.setRequiredError("Country is required field");
Button submit = new Button("Submit", new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
try {
binder.commit();
output.setValue((String) fields.getItemProperty("country").getValue());
}
catch (CommitException e) {
Notification.show("fail!");
}
}
});
form.addComponent(box);
form.addComponent(submit);
}
By default the ComboBox has allow null selection set to true. So there is a blank entry, which represents a null value selection. However the ComboBox's value when first rendered neither represents the null selection nor one of the items but an empty string.
So when I load the form and click the button the outcome is neither a failure, which it should be because I haven't selected anything yet, nor one of my selections.
This is causing an issue for me in a more advanced UI application but very much the same thing going on here.
Could anybody enlighten me as to what is happening here?
Many thanks,
Joe

So when I load the form and click the button the outcome is neither a
failure, which it should be because I haven't selected anything yet,
nor one of my selections.
Combobox is not empty as you think. It has default property value, that you set as empty string:
fields.addItemProperty("country", new ObjectProperty<String>(""));
Thus form pass validation, because empty string is also a value and empty string != null.
Change this row:
fields.addItemProperty("country", new ObjectProperty<String>(""));
to:
fields.addItemProperty("country", new ObjectProperty<String>(null, String.class));
box.setNullRepresentation("-- Select Country --");

Related

Update Wicket Text Field on onUpdate event

I have a component that extends TextField where a user can type an web address. I want that after the user type something (for example www.example.org) to change that value to something else (for exemple http://www.example.org)
I have tried this:
urlField = new TextFieldIndicatingError<String>("url", new PropertyModel<String>(this, "url"));
urlField.add(new AjaxFormComponentUpdatingBehavior("onblur") {
#Override
protected void onUpdate(AjaxRequestTarget target)
{
//url = "ABCDDEE";
urlField.getModel().setObject("AAAA");
}
});
but anything inside the onUpdate() doesn't seems to have an effect in the TextField's value.
What I'm doing wrong here?
You need to use target.add(urlField) to update it on the client side after setting its new model.

Manually typing in text in JavaFX Spinner is not updating the value (unless user presses ENTER)

It seems that the Spinner control does not update a manually typed-in value until the user explicitly presses enter. So, they could type in a value (not press enter) exit the control, and submit the form, and the value displayed in the spinner is NOT the value of the Spinner, it is the old value.
My idea was to add a listener to the lost focus event, but I can't see a way to gain access to the typed-in value?
spinner.focusedProperty().addListener((observable, oldValue, newValue) ->
{
//if focus lost
if(!newValue)
{
//somehow get the text the user typed in?
}
});
This is odd behavior, it seems to go against the convention of a GUI spinner control.
Unfortunately, Spinner doesn't behave as expected: in most OS, it should commit the edited value on focus lost. Even more unfortunate, it doesn't provide any configuration option to easily make it behave as expected.
So we have to manually commit the value in a listener to the focusedProperty. On the bright side, Spinner already has code doing so - it's private, though, we have to c&p it
/**
* c&p from Spinner
*/
private <T> void commitEditorText(Spinner<T> spinner) {
if (!spinner.isEditable()) return;
String text = spinner.getEditor().getText();
SpinnerValueFactory<T> valueFactory = spinner.getValueFactory();
if (valueFactory != null) {
StringConverter<T> converter = valueFactory.getConverter();
if (converter != null) {
T value = converter.fromString(text);
valueFactory.setValue(value);
}
}
}
// useage in client code
spinner.focusedProperty().addListener((s, ov, nv) -> {
if (nv) return;
//intuitive method on textField, has no effect, though
//spinner.getEditor().commitValue();
commitEditorText(spinner);
});
Note that there's a method
textField.commitValue()
which I would have expected to ... well ... commit the value, which has no effect. It's (final!) implemented to update the value of the textFormatter if available. Doesn't work in the Spinner, even if you use a textFormatter for validation. Might be some internal listener missing or the spinner not yet updated to the relatively new api - didn't dig, though.
Update
While playing around a bit more with TextFormatter I noticed that a formatter guarantees to commit on focusLost:
The value is updated when the control loses its focus or it is commited (TextField only)
Which indeed works as documented such that we could add a listener to the formatter's valueProperty to get notified whenever the value is committed:
TextField field = new TextField();
TextFormatter fieldFormatter = new TextFormatter(
TextFormatter.IDENTITY_STRING_CONVERTER, "initial");
field.setTextFormatter(fieldFormatter);
fieldFormatter.valueProperty().addListener((s, ov, nv) -> {
// do stuff that needs to be done on commit
} );
Triggers for a commit:
user hits ENTER
control looses focus
field.setText is called programmatically (this is undocumented behaviour!)
Coming back to the spinner: we can use this commit-on-focusLost behaviour of a formatter's value to force a commit on the spinnerFactory's value. Something like
// normal setup of spinner
SpinnerValueFactory factory = new IntegerSpinnerValueFactory(0, 10000, 0);
spinner.setValueFactory(factory);
spinner.setEditable(true);
// hook in a formatter with the same properties as the factory
TextFormatter formatter = new TextFormatter(factory.getConverter(), factory.getValue());
spinner.getEditor().setTextFormatter(formatter);
// bidi-bind the values
factory.valueProperty().bindBidirectional(formatter.valueProperty());
Note that editing (either typing or programmatically replacing/appending/pasting text) does not trigger a commit - so this cannot be used if commit-on-text-change is needed.
#kleopatra headed to a right direction, but the copy-paste solution feels awkward and the TextFormatter-based one did not work for me at all. So here's a shorter one, which forces Spinner to call it's private commitEditorText() as desired:
spinner.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
spinner.increment(0); // won't change value, but will commit editor
}
});
This is standard behavior for the control according to the documentation:
The editable property is used to specify whether user input is able to
be typed into the Spinner editor. If editable is true, user input will
be received once the user types and presses the Enter key. At this
point the input is passed to the SpinnerValueFactory converter
StringConverter.fromString(String) method. The returned value from
this call (of type T) is then sent to the
SpinnerValueFactory.setValue(Object) method. If the value is valid, it
will remain as the value. If it is invalid, the value factory will
need to react accordingly and back out this change.
Perhaps you could use a keyboard event to listen to and call the edit commit on the control as you go.
Here is an improved variant of Sergio's solution.
The initialize method will attach Sergio's code to all Spinners in the controller.
public void initialize(URL location, ResourceBundle resources) {
for (Field field : getClass().getDeclaredFields()) {
try {
Object obj = field.get(this);
if (obj != null && obj instanceof Spinner)
((Spinner) obj).focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue) {
((Spinner) obj).increment(0);
}
});
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
Using a listener should work. You can get access to the typed in value through the spinner's editor:
spinner.getEditor().getText();
I use an alternate approach - update it live while typing. This is my current implementation:
getEditor().textProperty().addListener { _, _, nv ->
// let the user clear the field without complaining
if(nv.isNotEmpty()) {
Double newValue = getValue()
try {
newValue = getValueFactory().getConverter().fromString(nv)
} catch (Exception e) { /* user typed an illegal character */ }
getValueFactory().setValue(newValue)
}
I used this approach
public class SpinnerFocusListener<T> implements ChangeListener<Boolean> {
private Spinner<T> spinner;
public SpinnerFocusListener(Spinner<T> spinner) {
super();
this.spinner = spinner;
this.spinner.getEditor().focusedProperty().addListener(this);
}
#Override
public void changed(ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
StringConverter<T>converter=spinner.getValueFactory().getConverter();
TextField editor=spinner.getEditor();
String text=editor.getText();
try {
T value=converter.fromString(text);
spinner.getValueFactory().setValue(value);
}catch(Throwable ex) {
editor.setText(converter.toString(spinner.getValue()));
}
}
}

How to validate fields in vaadin made form

I am making a Java project with vaadin. Right now I have a user registration form looking like that:
public class RegistrationComponent extends CustomComponent implements View {
public static final String VIEW_NAME = "Registration";
public RegistrationComponent(){
Panel panel = new Panel("Registration Form");
panel.setSizeUndefined();
FormLayout content = new FormLayout();
CheckBox checkBox1, checkBox2, checkBox3;
checkBox1 = new CheckBox("Check Box 1");
checkBox2 = new CheckBox("Check Box 2");
checkBox3 = new CheckBox("Check Box 3");
checkBox1.setRequired(true);
checkBox2.setRequired(true);
TextField mailTextField = new TextField("Email Address");
TextField passwordTextField = new TextField("Password");
TextField confirmPasswordTextField = new TextField("Confirm Password");
final Button submitButton = new Button("Submit");
content.addComponent(mailTextField);
content.addComponent(passwordTextField);
content.addComponent(confirmPasswordTextField);
content.addComponent(checkBox1);
content.addComponent(checkBox2);
content.addComponent(checkBox3);
content.addComponent(submitButton);
content.setSizeUndefined(); // Shrink to fit
content.setMargin(true);
panel.setContent(content);
setCompositionRoot(panel);
//listeners:
submitButton.addClickListener(new Button.ClickListener() {
public void buttonClick(Button.ClickEvent event) {
//
}
});
}
#Override
public void enter(ViewChangeListener.ViewChangeEvent event){
//
}
}
Of course, the form doesn't do anything other than being displayed.
What I wanna do, is make Vaadin display error messages next to fields if some requirements are not met. The requirements themselves are not that important (lets say I want email field to contain at least 8 characters). What I wanna know, is: is there any simple built-in way to do that? I was here:
https://vaadin.com/api/com/vaadin/data/Validator.html
but I dont understand how to use a validator, or even if that is what I want to use. I've been looking all over google for usage examples, but so far with no success. Thanks for help!
Vaadin 7
The following applies to Vaadin 7. The validate() method has been removed in Vaadin 8.
All Field types in Vaadin implement the Validatable interface which has the addValidator method that accepts an implementation of Validator as parameter.
So to add a validator that checks the length of the value of a TextField, you would do this:
TextField textField = new TextField();
textField.addValidator(
new StringLengthValidator(
"Must be between 2 and 10 characters in length", 2, 10, false));
Vaadin fields have built-in functionality for displaying the validation errors to the user. By default, the field will be highlighted in red and an exclamation mark will appear next to the field, hovering over this will show a more detailed message to the user.
Automatic Validation
By default, the field will now validate on the next server request which contains a changed value for the field to the server. If the field is set to 'immediate', this will happen when the field looses focus. If the field is not immediate, validation will happen when some other UI action triggers a request back to the server.
Explicit Validation
Sometimes, you may want to exercise more control over when validation happens and when validation errors are displayed to the user. Automatic validation can be disabled by setting validationVisible to false.
textField.setValidationVisible(false);
When you are ready to validate the field (e.g. in a button click listener) you can explicitly call the validate (you can also use commit() if it is a buffered field) method on the TextField instance to trigger validation. validate will throw an InvalidValueException if the value is invalid. If you want to use the builtin display of validation errors included in the TextField component you will also have to set validationVisible back to true.
try {
textField.validate();
} catch (Validator.InvalidValueException ex) {
textField.setValidationVisible(true);
Notification.show("Invalid value!");
}
Note that once validationVisbible is set back to true, validation will happen implicitly so you must remember to set it back to false on the next request if you want to maintain explicit control over validation.
Validation Messages
Individual validation messages can be extracted from the instance of Validator.InvalidValueException which is thrown when validate() or commit() is called.
try {
textField.validate();
} catch (Validator.InvalidValueException ex) {
for (Validator.InvalidValueException cause: ex.getCauses()) {
System.err.println(cause.getMessage());
}
}
Validators
Validators implement the Validator interface and there are several useful validators shipped with Vaadin. Check out the API docs for more information on these: https://vaadin.com/api/7.4.5/com/vaadin/data/Validator.html
Custom validators are easy to implement, here is an example taken from the Book of Vaadin:
class MyValidator implements Validator {
#Override
public void validate(Object value)
throws InvalidValueException {
if (!(value instanceof String &&
((String)value).equals("hello")))
throw new InvalidValueException("You're impolite");
}
}
final TextField field = new TextField("Say hello");
field.addValidator(new MyValidator());
field.setImmediate(true);
layout.addComponent(field);
Problem solved,
Apparently I wasn't looking deep enough before. Here it comes:
field.addValidator(new StringLengthValidator("The name must be 1-10 letters (was {0})",1, 10, true));
all details here:
https://vaadin.com/book/-/page/components.fields.html
Vaadin 8
Using Vaadin 8 com.vaadin.data.Binder easily you can validate your fields. See Binding Data to Forms in the manual.
Create a TextField and a binder to validate the text field.
public class MyPage extends VerticalLayout{
TextField investorCode = new TextField();
Binder<MyBean> beanBinder = new Binder<MyBean>();
//Info : MyBean class contains getter and setter to store values of textField.
public MyPage (){
investorCode.addValueChangeListener(e->valueChange(e));
addComponent(investorCode);
bindToBean();
}
private void bindToBean() {
beanBinder.forField(investorCode)
.asRequired("Field cannot be empty")
.withValidator(investorCode -> investorCode.length() > 0,"Code shold be atleast 1 character long").bind(MyBean::getInvestorCode,MyBean::setInvestorCode);
}
//rest of the code .....
private void valueChange(ValueChangeEvent<String> e) {
beanBinder.validate();
}
}
Call validate() from binder will invoke the validation action.
beanBinder.validate();
to validate the filed. You can call this from anywhere in the page. I used to call this on value change or on a button click.

Proper way to reflect data changes in JFace viewers

I am looking into JFace for Eclipse development. I made a plugin to act as a dummy content provider for a ComboViewer. This provider essentially provides the data model as an ArrayList of hardcoded values. Anyway, I tried to understand the approach.
I set the model on the ComboViewer via the comboViewer.setInput(list) method.
On the press of a button I call another object's method that updates the list I passed as input to the ComboViewer (adds another element) and I call comboViewer.refresh to reflect the change, but nothing happens.
Turns out:
I need to call comboViewer.setInput(list) with the updated list to see the changes in the data (i.e. the previous addition) in my UI combo. I found that comboViewer.refresh reflects any updates only if I get the a hold of comboViewer's passed as input Object and modify that. I.e. if I do:
List<SomeObject> data = ((List<SomeObject>)(comboViewer.getInput()));
data.add(new SomeObject("aaa","cccc"));
comboViewer.refresh();
Only like this the data are refreshed. But I don't understand what is the proper way to use these APIs.
Am I supposed to ever get a hold and modify the object I pass in the setInput method? It feels I should not be doing it. So what is the purpose of refresh?
What is the proper way to do updates of the data that are provided to the Viewers?
The proper way to reflect changes is to call refresh. The list
String[] values = {"1","2","3"};
List<String> list = new ArrayList<String>(Arrays.asList(values));
create components
final ComboViewer comboViewer = new ComboViewer(shell, SWT.DROP_DOWN);
comboViewer.setLabelProvider(new LabelProvider());
comboViewer.setContentProvider(new ArrayContentProvider());
comboViewer.setInput(list);
Button button1 = new Button(shell, SWT.PUSH);
button1.setText("Button 5");
button1.addSelectionListener(new SelectionListener(){
#Override
public void widgetSelected(SelectionEvent e) {
// TODO Auto-generated method stub
System.out.println("Button 5");
list.add("4");
comboViewer.refresh();
}
#Override
public void widgetDefaultSelected(SelectionEvent e) {
// TODO Auto-generated method stub
}
});
when you push the button the combo viewer is updated.

Is it possible to nest forms in Wicket that are independent of each other?

Is it possible to nest forms in Wicket that are independent of each other? I want to have a form with a submit button and a cancel button. Both buttons should direct the user to the same page (let's call it Foo). The submit button should send some info to the server first; the cancel button should do nothing.
Here's a really simplified version of my existing code:
Form form = new Form() {
public void onSubmit()
{
PageParameters params = new PageParameters();
params.put("DocumentID", docID);
setResponsePage(Foo.class, params);
}
};
DropDownChoice<String> ddc = new DropDownChoice<String>("name", new PropertyModel<String>(this, "nameSelection"), names);
ddc.setRequired(true);
final Button submitButton = new Button("Submit") {
public void onSubmit() { doSubmitStuff(true); }
};
final Button cancelButton = new Button("Cancel") {
public void onSubmit() { doSubmitStuff(false); }
};
form.add(ddc);
form.add(submitButton);
form.add(cancelButton);
form.add(new FeedbackPanel("validationMessages"));
The problem is, I just added a validator, and it fires even if I press the cancel button, since the cancel button is attached to the same form as everything else. This could be avoided if the cancel button were in a separate form. As far as I know, I can't create a separate form because — due to the structure of the HTML — the separate form would be under the existing form in the component hierarchy.
Can I make the forms separate somehow in spite of the hierarchy? Or is there some other solution I can use?
EDIT:
In response to Don Roby's comment, this is a bit closer to what my code looked like back when I was trying setDefaultFormProcessing():
Form<Object> theForm = new Form<Object>("theForm") {
public void onSubmit()
{
PageParameters params = new PageParameters();
params.put("DocumentID", docID);
setResponsePage(Foo.class, params);
}
};
final CheckBox checkbox = new CheckBox("checkbox", new PropertyModel<Boolean>(this, "something"));
checkbox.add(new PermissionsValidator());
theForm.add(checkbox);
final Button saveButton = new Button("Save") {
public void onSubmit()
{ someMethod(true); }
};
final Button cancelButton = new Button("Cancel") {
public void onSubmit()
{ someMethod(false); }
};
cancelButton.setDefaultFormProcessing(false);
theForm.add(saveButton);
theForm.add(cancelButton);
theForm.add(new FeedbackPanel("validationMessages"));
There is an even simpler solution: call the setDefaultFormProcessing method on the cancel button with false as a parameter:
cancelButton.setDefaultFormProcessing(false);
This way, clicking the cancel button will bypass the form validation (and model updating), directly calling the onSubmit function.
It is possible to "nest" forms in wicket.
See this wiki entry
for some notes on how it works and this wiki entry for how it interacts with validation.
But for what you're after, the answer from Jawher should have worked and is much simpler.
Look at this example code for hints on getting that working.
I'm wondering if you've simplified your code too far in this posting. Can you produce a sample small enough to post that definitely has the problem?

Categories

Resources