5

The problem here is that I wanna make sure that the user doesn't enter any strings or text especially that I need to enter his choice into a database later so I don't things to get messed up in the database's part, here is part of code which is the view I wish to use the textview with restricted Integers (specifically the amount am field). PS: I'm still new to both JavaFX and TornadoFX so hope this doesn't sound like a rather silly question.

My Code:

package com.company.view

import javafx.beans.property.SimpleIntegerProperty
import javafx.scene.control.CheckBox
import tornadofx.*
import javafx.scene.control.TextField
import javafx.util.converter.NumberStringConverter
import java.sql.Connection

class Add: View() {
    override val root = Form()
    private val mainMenu: MainMenu by inject()
    private var cname: TextField by singleAssign()
    private var address: TextField by singleAssign()
    private var sname: TextField by singleAssign()
    private var ch: CheckBox by singleAssign()
    private var am: TextField by singleAssign()
    var conn: Connection?= mainMenu.conn

    init {
        with(root) {
            vbox(30.0) {
                fieldset("Enter Your Info below") {
                    field("Enter The Customer's Name") {
                            cname = textfield()
                    }
                    field("Enter the Customer's address") {
                        address = textfield()
                    }
                    field("Enter Bought Stock's Name") {
                        sname = textfield()
                    }
                    field("Do you wish to pay now?") {
                        ch = checkbox()
                    }
                    field("Enter the amount you wish to buy"){
                        am = textfield()
                    }
                    button("Submit")
                    {
                        setOnAction {
                            addPayment(cname.text, address.text, sname.text, ch.isSelected, am.text)
                        }
                    }
                }
            }
        }
    }

   private fun addPayment(cusName: String, caddress: String, stname: String, che: Boolean,am: String){
//required code for inserting into the database here.


    }
}
Adam Arold
  • 29,285
  • 22
  • 112
  • 207
Ali Haroon
  • 83
  • 1
  • 4
  • 12
  • I don't have much experience using tornadofx, but I don't think that it has that option out of the box, but you could make simple function which checks if input text is integer. – FilipRistic Feb 03 '18 at 20:52
  • true but it would be kind of a waste to do it if there is something that is already implemented – Ali Haroon Feb 03 '18 at 21:16

2 Answers2

9

You can use the filterInput extension function we've added to TextField and check that the text after the addition is in int. If it's not, deny the last input change:

textfield {
    filterInput { it.controlNewText.isInt() } 
}

On another note, you really need to look into ItemViewModel. It's an anti-pattern to assign each input element to a variable and extract the values from the input values on submit. Your code will be a lot cleaner and easier to reason about and refactor later if you use view models.

PS: The filterInput function is available in the soon to be released TornadoFX 1.7.15, in the mean time you can add this extension function to your project:

fun TextInputControl.filterInput(discriminator: (TextFormatter.Change) -> Boolean) {
    textFormatter = TextFormatter<Any>(CustomTextFilter(discriminator))
}
Edvin Syse
  • 7,267
  • 18
  • 24
  • Yeah saw the itemViewModel but the thing is that I couldn't really relate as how to use it along with database queries – Ali Haroon Feb 03 '18 at 21:10
  • 1
    That's no problem at all. I'll create a screencast on that soon :) – Edvin Syse Feb 03 '18 at 21:25
  • That would be much appreciated, and thanks for the answer earlier but the filterInput is giving me Unresolved reference after entering it as instructed above, isn't supposed to be in this form : am = textfield{ filterInput{ it.controlNewText.isInt() } } – Ali Haroon Feb 03 '18 at 21:35
  • Ah, I forgot - it was just added to the unreleased 1.7.15 :) I updated the answer with a workaround :) – Edvin Syse Feb 04 '18 at 21:47
  • I would like to point out that this does not make it trivial to enter a negative number: typing the - is rejected as input, so you have to type the value and then go back to prefix the minus. – RDM Dec 18 '18 at 14:44
1

From your example it seems like that you'd want to use a PropertySheet which comes from ControlsFX. I use it in production and it works well with TornadoFX.

Here is an example from the samples project which you can peruse. This will let you edit and bind multiple types not just numbers:

public class PropertySheetExample extends VBox {
    private static Map<String, Object> customDataMap = new LinkedHashMap<>();
    static {
        customDataMap.put("Group 1#My Text", "Same text"); // Creates a TextField in property sheet
        customDataMap.put("Group 1#My Date", LocalDate.of(2000, Month.JANUARY, 1)); // Creates a DatePicker
        customDataMap.put("Group 2#My Enum Choice", SomeEnumType.EnumValue); // Creates a ChoiceBox
        customDataMap.put("Group 2#My Boolean", false); // Creates a CheckBox
        customDataMap.put("Group 2#My Number", 500); // Creates a NumericField
    }

    class CustomPropertyItem implements PropertySheet.Item {
        private String key;
        private String category, name;

        public CustomPropertyItem(String key) {
            this.key = key;
            String[] skey = key.split("#");
            category = skey[0];
            name = skey[1];
        }

        @Override
        public Class<?> getType() {
            return customDataMap.get(key).getClass();
        }

        @Override
        public String getCategory() {
            return category;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getDescription() {
            return null;
        }

        @Override
        public Object getValue() {
            return customDataMap.get(key);
        }

        @Override
        public void setValue(Object value) {
            customDataMap.put(key, value);
        }
    }

    public PropertySheetExample {
        ObservableList<PropertySheet.Item> list = FXCollections.observableArrayList();
        for (String key : customDataMap.keySet())
            list.add(new CustomPropertyItem(key));

        PropertySheet propertySheet = new PropertySheet(list);
        VBox.setVgrow(propertySheet, Priority.ALWAYS);
        getChildren().add(propertySheet);
    }
}

You can also take a look at this question for more info.

Adam Arold
  • 29,285
  • 22
  • 112
  • 207