JavaFx: How to validate mutilple TextFields creati

2019-07-29 00:41发布

问题:

I am creating multiple TextFields at run time using for-loop and adding them to inside Gridpane(which has 8 columns) like this:

public static GridPane table(int rows){
            GridPane table = new GridPane();

            for(int i=0; i<rows; i++){
            JFXTextField textField1 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            JFXTextField textField2 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            JFXTextField textField3 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            JFXTextField textField4 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            JFXTextField textField5 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            JFXTextField textField6 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            JFXTextField textField7 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            JFXTextField textField8 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);

            //add them to the GridPane
            table.add(textField1, 0, i+1);
            table.add(textField2, 1, i+1);
            table.add(textField3, 2, i+1);
            table.add(textField4, 3, i+1);
            table.add(textField5, 4, i+1);
            table.add(textField6, 5, i+1);
            table.add(textField7, 6, i+1);
            table.add(textField8, 7, i+1);
         }
        return table;
    }

Next I'm creating another method to return component from table at specific row and column like this:

public static Node getComponent (int row, int column, GridPane table) {
         for (Node component : table.getChildren()) { // loop through every node in the table
             if(GridPane.getRowIndex(component) == row && 
                             GridPane.getColumnIndex(component) == column) {
                 return component;
             }
         }

         return null;
     }

Problem is here: I want to validate each of the TextField, so if user forget to write in any of the TextField, I want to disable the Button, for this purpose I'm using binding like this:

 private void validatingGrid() {
        GridPane table = (GridPane) anchorPane().getChildren().get(0);

        for(int i=1 ; i<=comboBox().getValue(); i++){
            JFXTextField text0 = ((JFXTextField)getComponent (i, 0, table));
            JFXTextField text1 = ((JFXTextField)getComponent (i, 1, table));
            JFXTextField text2 = ((JFXTextField)getComponent (i, 2, table));
            JFXTextField text3 = ((JFXTextField)getComponent (i, 3, table));
            JFXTextField text4 = ((JFXTextField)getComponent (i, 4, table));
            JFXTextField text5 = ((JFXTextField)getComponent (i, 5, table));
            JFXTextField text6 = ((JFXTextField)getComponent (i, 6, table));
            JFXTextField text7 = ((JFXTextField)getComponent (i, 7, table));

                button.disableProperty().bind(
                        Bindings.isEmpty(text0.textProperty())
                        .or(Bindings.isEmpty(text1.textProperty()))
                        .or(Bindings.isEmpty(text2.textProperty()))
                        .or(Bindings.isEmpty(text3.textProperty()))
                        .or(Bindings.isEmpty(text4.textProperty()))
                        .or(Bindings.isEmpty(text5.textProperty()))
                        .or(Bindings.isEmpty(text6.textProperty()))
                        .or(Bindings.isEmpty(text7.textProperty()))
                    );
                }
     }

But what's happening is it's only validating last row, let say if I create 3 rows of textfeilds in the Gridpane, so it's only validating 3rd row not 1st and 2nd rows and on the basis of 3rd row entries it's enabling the button but I want after validating all of the rows it should enable button otherwise not. Please help me how can I achieve this.

回答1:

Your binding logic is correct. However, the problem because of the for loop [for(int i=1 ; i<=comboBox().getValue(); i++)], which ruins your work. All TextFields are at column index 0 and the only thing changes is the row index. So you should use getComponent(i, 0, table); for all TextFields in your for loop without changing the column index to 1 , 2 .. and so on. But that also won't solve the problem because in every loop you're assigning ALL TextFields to the same index and then overwrites it in every loop until all of them points to the TextField at index comboBox().getValue() and column 0 (That's why it's working for the last row as you mentioned).

I would suggest different approach, something like this:

First You need a method to check if all other TextFields are filled/ not empty:

/**
* Check if all the TextFields are filled and not empty
* @param table
*/
 private static boolean isAllFilled(GridPane table){
    for(Node node : table.getChildren()){ // cycle through every component in the table (GridPane)
        if(node instanceof TextField){ // if it's a TextField
        // after removing the leading spaces, check if it's empty
            if(((TextField)node).getText().trim().isEmpty()){
                    return false; // if so, return false
            }
        }       
    }
    return true;
 }

Secondly, Listen to the Text Changes for every TextField in the Table, and with every change, check if all other TextField are filled / not empty:

/**
* To Validate the Table (GridPane)
* This method should be added to the tabPane change listener
* @param table
* @param button
*/
private void validateTable(GridPane table, Button button) {

   for(Node node : table.getChildren()){ // cycle through every component in the table (GridPane)
      if(node instanceof TextField){ // if it's a TextField
        ((TextField)node).textProperty().addListener((obs, old, newV)->{ // add a change listener to every TextField
        // then check if the new value is not empty AND all other TextFields are not empty
          if(!newV.trim().isEmpty()&&isAllFilled(table)){ 
             button.setDisable(false); // then make the button active again
          }
          else{
              button.setDisable(true); // or else, make it disable until it achieves the required condition 
          }
      });
   }    
}

Also, you need to set the button to disable once after its creation.

Button button = new Button("Test"); 
button.setDisable(true);

Finally, you need to add the method in the tabPane Change Listener Block:

tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
      .........
      .........
      .........
      validateTable((GridPane) anchorPane().getChildren().get(0), test);
}

Test