Validate vaadin combobox custom input (integers only) - java

I have an input form with a combobox which displays integer values. I want the user to be able to add new integer values to this combobox. I need to validate whether the input are only numbers (and not letters) - if it isnt a valid integer I want to display an error message.
For textfields i figured it out quickly - i get an error message even while typing a "wrong" input! But i cannot find a solution for the combobox in combination with the addCustomValueSetListener.
I'm using Vaadin 14.1.21 and Java JDK+JRE 1.8.
Right now if I input a custom value with letters, I don't get an error message displayed beneath the box and it just silently ignores the input value when I want to "save" it / store it in the database.
public class MyForm extends Div {
private TextField tf;
tf =new TextField("TF");
tf.setWidth("100%");
tf.setRequired(true);
tf.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
tf.setValueChangeMode(ValueChangeMode.EAGER);
private ComboBox<Integer> combo_int;
combo_int= new ComboBox<>();
combo_int.setItems(114, 12383, 65432189);
combo_int.setLabel("Some ID");
combo_int.addCustomValueSetListener(
event -> combo_int.setValue(Integer.parseInt(event.getDetail()))
// since I need to parse new values here, I cannot use a validator upon binding
);
binder = new BeanValidationBinder<>(MyData.class);
binder.forField(tf)
.withNullRepresentation("")
.withConverter(new StringToIntegerConverter("needs to be integer!"))
.bind("tf_data_integer");
binder.forField(combo_int)
.bind("integer_data");
}

So i figured out a very dirty McGyver solution... but it still does not work 100%.
The main problem being that:
I have to parse (and hence validate) custom input in the addCustomValueSetListener
I cannot do it at the bean-binder as the custom input comes as a
String and the bean-binder expectes an Integer
Using withConverter(StringToInteger) does not work as I still want
to be able to select an Integer item
from the Drop-Down menu.
So I had to
build my own warning label
reset the label manually upon switching
between objects which are displayed in the form
use setInvalid to highlight the wrong input & reset it after
highjack the Validator to reset the status_label (to fix the issue of the label remaining if the user used the drop-down menu after selecting a wrong one - the label stayed...)
I still have the problem that binder.hasChanges() doesn't register an invalid input as a change as we catch it beforehand in the parser and it never makes it to the binder.
Perhaps I shall find a solution for that too, tomorrow.
public class MyForm extends Div {
private Label status_label = new Label();
private TextField tf;
tf =new TextField("TF");
tf.setWidth("100%");
tf.setRequired(true);
tf.addThemeVariants(TextFieldVariant.LUMO_ALIGN_RIGHT);
tf.setValueChangeMode(ValueChangeMode.EAGER);
private ComboBox<Integer> combo_int;
combo_int= new ComboBox<>();
combo_int.setItems(114, 12383, 65432189);
combo_int.setLabel("Some ID");
combo_int.addCustomValueSetListener((event -> {
if (isInteger(event.getDetail())){
status_label.setText(""); // reset on success
warengruppen_id.setValue(Integer.parseInt(event.getDetail()));
warengruppen_id.setInvalid(false);
} else {
status_label.setText("Custom Format-Error!"); // set to error
status_label.getStyle().set("color", "red");
combo_int.setInvalid(true); // red background coloring
}
}
);
binder = new BeanValidationBinder<>(MyData.class);
binder.forField(tf)
.withNullRepresentation("")
.withConverter(new StringToIntegerConverter("needs to be integer!"))
.bind("tf_data_integer");
binder.forField(combo_int).
withValidator(event -> {
// we highjack the Validator to reset the field on correct input
status_label.setText("");
return true; // never raise the validator message
}, "")
.withNullRepresentation(null)
.bind("integer_data");
}

My final solution was to use Strings to display them in the frontend and build a custom Converter where I disable the "setGroupingUsed" to stop Vaadin from inserting thousands-seperators into my IDs in the UI.
Turns out it was easier than I thought, I just had to use a different data format for the UI than for the backend + a custom converter.
private static class Id_StringToInteger_Converter extends StringToIntegerConverter {
public Id_StringToInteger_Converter() {
super("Input has to be an Integer!");
}
#Override
protected NumberFormat getFormat(Locale locale) {
final NumberFormat format = super.getFormat(locale);
format.setGroupingUsed(false); // disable thousands-seperator!
return format;
}
}
combo_int= new ComboBox<>();
combo_int.setItems("6583212", "114514", "879278");
combo_int.setLabel("Some ID");
combo_int.addCustomValueSetListener(event -> combo_int.setValue(event.getDetail()));
binder.forField(kreditoren_id)
.withNullRepresentation("")
.withConverter(new Id_StringToInteger_Converter())
.bind("kreditoren_id");

Related

JavaFX: Change a TextField or Text object in a controller class while maintaining the separation of view and controller

I'm struggling to find a way to alter the TextField and Text objects of the view class from the controller class without just passing the TextField and Texts to the controller and then altering them using commands such as textField.setText() which I know is in breach of the whole separation idea. I am creating a game to guess a number between 1 and 50.
Example:
View Class:
This class will set up the stage with Labels, Texts, and TextFields for user input, as well as buttons. There is a button to guess if the number you entered into the TextField is correct. When the guess button is clicked I call for the setOnAction method in the view class which then calls a method from the controller class to handle the events. I realise that I cannot just pass in the TextField to the Controller because you need to keep the view separate from the controller so I can pass in the TextField.getText() and Text.getText() as shown bellow in order to convert it to a string first which keeps them separate. In this example enteredNumber is a TextField and result is a Text.
guessButton.setOnAction(e -> guessingGameController.guessButtonAction(enteredNumber.getText(), result.getText()));
I then read and process the data in the controller class:
public void guessButtonAction(String enteredNumber, String result) {
int enteredValue = Integer.parseInt(enteredNumber);
if (enteredValue == randomNum) {
result = ("YOU WON!!!");
}
else if (enteredValue != randomNum) {
result = ("Unlucky the number was " + randomNum);
}
}
I realise that I can simply return the String result from the controller and then have the view class make the Text resultBox set the text like this:
guessButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
String result = guessButtonAction(
enteredNumber.getText(), result.getText());
resultBox.setTex(result);
}
});
But There are a few buttons that would have to return more then one variables meaning I'd have to return an array which I feel complicates things too much. Is there any other way of doing this without being so messy and maintaining the separation of the GUI and the processing of data? I apologise for the long question, I hope I explained it well enough.

Java showInputDialog select custom text

I have rename dialog for rename file
String renameTo = JOptionPane.showInputDialog(gui, "New Name", currentFile.getName());
it works this way, but I have a problem.
the problem is that I set the default value with the extension of the file
but I just want the file name to be selected.
sample : my file name = yusuf.png
I want select only yusuf like;
There is a lot going on inside JOptionPane, it's one of the things that makes it so powerful, it also makes it a little inflexible to.
Two immediate problems are apparent...
You can't gain direct access to the JTextField been used to get input from the user
The JOptionPane wants to control which components have focus when the dialog is first shown.
Setting up the JTextField is actually straight forward...
String text = "yusuf.png";
int endIndex = text.lastIndexOf(".");
JTextField field = new JTextField(text, 20);
if (endIndex > 0) {
field.setSelectionStart(0);
field.setSelectionEnd(endIndex);
} else {
field.selectAll();
}
This will basically select all the text from the start of the String up to the last . or all the text if no . can be found.
The difficult part now is taking back focus control from the JOptionPane
// Make a basic JOptionPane instance
JOptionPane pane = new JOptionPane(field,
JOptionPane.PLAIN_MESSAGE,
JOptionPane.OK_CANCEL_OPTION,
null);
// Use it's own dialog creation process, it's simpler this way
JDialog dialog = pane.createDialog("Rename");
// When the window is displayed, we want to "steal"
// focus from what the `JOptionPane` has set
// and apply it to our text field
dialog.addWindowListener(new WindowAdapter() {
#Override
public void windowActivated(WindowEvent e) {
// Set a small "delayed" action
// to occur at some point in the future...
// This way we can circumvent the JOptionPane's
// focus control
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
field.requestFocusInWindow();
}
});
}
});
// Put it on the screen...
dialog.setVisible(true);
dialog.dispose();
// Get the resulting action (what button was activated)
Object value = pane.getValue();
if (value instanceof Integer) {
int result = (int)value;
// OK was actioned, get the new name
if (result == JOptionPane.OK_OPTION) {
String newName = field.getText();
System.out.println("newName = " + newName);
}
}
And, crossing our fingers, we end up with something looking like...
Personally, I'd wrap this up in a nice reusable class/method call which returned the new text or null based on the action of the user, but that's me
Isn't there an easier way?
Of course, I just like showing you the most difficult solution possible ... 😳 (sarcasm) ... it's kind of why I suggested wrapping it up in it's own utility class, so you can re-use it later 😉

Read JTextArea For Jazzy Spell Checker API

Question: Trying to get the same effect as the code below only with JTextArea so I want the JTextArea to be read and spelling suggestions to be recommended every time the user types a new misspelt word.
Below is the working example with 'System.in' which works well.
(Vars userField = JTextArea & dic.txt is a list of the english language for the system to use for suggestions)
CODE (1)
public SpellCheckExample() {
try {
SpellDictionary dictionary = new SpellDictionaryHashMap(new File(dic.txt));
spellCheck = new SpellChecker(dictionary);
spellCheck.addSpellCheckListener(this);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (true) {
System.out.print("Enter text to spell check: ");
String line = in.readLine();
if (line.length() <= 0)
break;
spellCheck.checkSpelling(new StringWordTokenizer(line));
}
} catch (Exception e) {
e.printStackTrace();
}
}
What I have Been trying:
CODE (2)
public void spellChecker() throws IOException{
String userName = System.getProperty("user.home");
SpellDictionary dictionary = new SpellDictionaryHashMap(new File(userName+"/NetBeansProjects/"+"/project/src/dic.txt"));
SpellChecker spellCheck = new SpellChecker(dictionary);
spellCheck.addSpellCheckListener(this);
try{
StringReader sr = new StringReader(userField.getText());
BufferedReader br = new BufferedReader(sr);
while(true){
String line = br.readLine();
if(line.length()<=0)
break;
spellCheck.checkSpelling(new StringWordTokenizer(line));
}
}catch(IOException e){
e.printStackTrace();
}
}
March 3rd 2016 (Update)
public void spellChecker() throws IOException{
// getting context from my dic.txt file for the suggestions etc.
SpellDictionary dictionary = new SpellDictionaryHashMap(new File("/Users/myname/NetBeansProjects/LifeSaver/src/dic.txt"));
SpellChecker spellCheck = new SpellChecker(dictionary);
// jt = JTextField already defined in constructors and attemtpting to pass this into system and
InputStream is = new ByteArrayInputStream(jt.getText().getBytes(Charset.forName("UTF-8")));
//spellCheck.checkSpelling(new StringWordTokenizer(line)); ""ORIGINAL"""
// reccomending cast to wordfinder
spellCheck.checkSpelling(new StringWordTokenizer(is);
}
You don't want to try to drop console UI code into an event-driven GUI, as it will never work like that. Instead you need to use GUI events to trigger your actions, not readln's.
The first thing you must decide on is which event you wish to use to trigger your spell check. For my money, I'd get the user's input in a JTextField, not a JTextArea since with the former, we can easily trap <enter> key presses by adding an ActionListener on the JTextField. You can always use both, and then once the text is spell checked, move it to the JTextArea, but this is exactly what I'd recommend:
use a JTextField,
add an ActionListener to the JTextField to be notified whenever the field has focus and enter is pressed,
within this listener, extract the text from the JTextField, by calling getText() on the field
Then run your spell check code on extracted text,
and output the result into a nearby JTextArea.
Take a look at Concurrency in Swing for reasons why your current approach won't work, then have a look at Listening for Changes on a Document and Implementing a Document Filter for some possible solutions
As someone is bound to mention it, DON'T use a KeyListener, it's not an appropriate solution for the problem
Put simpler, Swing is a single threaded, event driven framework. So anything you do which blocks the Event Dispatching Thread, will prevent it from processing new events, including paint events, making your UI unresponsive
As an event driven environment, you need to register interested in been notified when some event occurs (this is an example of Observer Pattern) and then take appropriate actions based on those events.
Remember though, you can not make changes to a Document via a DocumentListener, so be careful there

adding a field name as a method parameter

I have a Swing form where I want to detect edited field data so I can update the data via a web service. There doesn't seem to be a simple way to do this as there are a plethora of medium to complex code examples for this, most of which are over my head! However, I think I found a simple solution that will fit my needs and I wanted to run it by the group for input / suggestions. FYI: it may not matter but know that I am using NetBeans so things like listeners are auto-coded by the app.
Step 1: When I load the form, I am saving all the data to a Class array so I know the starting point of the each field. Here is the code for that load:
public void coSetSearchDetail(String coDetail){
String[] text;
System.out.println("SingleCO Result: "+ coDetail);
text = coDetail.split("\\|");
txtName_CoDetail.setText(text[1]);
txtAddr_CoDetail.setText(text[2]);
txtAddr2_CoDetail.setText(text[3]);
txtCity_CoDetail.setText(text[4]);
txtState_CoDetail.setText(text[5]);
txtZip_CoDetail.setText(text[6]);
stringarrayCoDetails[0] = text[0];
stringarrayCoDetails[1] = text[1];
stringarrayCoDetails[2] = text[2];
stringarrayCoDetails[3] = text[3];
stringarrayCoDetails[4] = text[4];
stringarrayCoDetails[5] = text[5];
stringarrayCoDetails[6] = text[6];
java.awt.CardLayout card = (java.awt.CardLayout)pnlMain.getLayout();
card.show(pnlMain, "pnlCoDetail");
}
Step 2: I created a Lost Focus event listener for one field and am testing the current value of the field against the array:
private void txtName_CoDetailFocusLost(java.awt.event.FocusEvent evt) {
if (!(txtName_CoDetail.getText().equals(stringarrayCoDetails[1]))){
createEditBorder();
}
}
private void createEditBorder (){
Border border = BorderFactory.createLineBorder(Color.RED, 2);
txtName_CoDetail.setBorder(border);
}
Besides the general question of "is this an OK approach?", I would like to be able to pass the field name to the createEditBorder method so the listener for each data field can call it and I have one method for "edited text" formatting.

InputVerifier and multiple fields

I am working on a form that provides "real-time" validation to the user and I have one problem.
The goal is to put a label near the field (in this case a JSpinner), to show the user if the data is accepted or denied, in the same way that javascript-based validators do.
The problem is that for archieving this, I need to set the value for the corresponding label and the only way I have found to do this is to create as many verifiers as fields, this way:
class MyVerifier extends InputVerifier{
static final double MAX_VALUE = 30;
#Override
public boolean verify(JComponent input) {
JTextField tf = (JTextField) input;
Double value = Double.parseDouble(tf.getText().replace(',', '.'));
return (value>1);
}
#Override
public boolean shouldYieldFocus(JComponent input) {
boolean isValid = super.shouldYieldFocus(input);
if (isValid) {
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("resources/accept.png")));
jLabel1.setText("");
} else {
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("resources/exclamation.png")));
jLabel1.setText("The number of items must be greater than 1");
}
return true;
}
}
Then, the same code for jLabel2... It must be another way to do this.
Thanks in advance.
You could have a Hashmap for the text field and its related label component. Then in the shouldYieldFocus method you retrieve the related label for the text field being validated. You can then set the text/icon of the label appropriately.
You would probably also need a secound Hashmap containg the label and the text message for the error.
You could also use a JDialog as a popup next to the JComponent you are validating. This popup JDialog will have a JLabel that will encapsulate the message you want to display next to the respective Jcomponent. All you have to do is to calculate the position of the popup relative to the Jcomponent you are validating.
You can find a good example here

Categories

Resources