1

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.

Junaid
  • 664
  • 5
  • 18
  • 35
  • What is `JFXTextField`? Why not using `TextField` – Yahya May 30 '17 at 15:23
  • @Yahya Learn google-fu this would quickly brought up the fact that it's a control from [JFoenix](https://github.com/jfoenixadmin/JFoenix) and using it is basically a design choice. Although in this case it just makes the description more complicated than necessary... – fabian May 30 '17 at 16:08

1 Answers1

3

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

Testing

Yahya
  • 13,349
  • 6
  • 30
  • 42
  • it solved my problem and thanks for the explanation, Merci @Yahya :) – Junaid May 31 '17 at 07:46
  • Another thing @Yahya what if in **Tab B** I've another Table let say TableA in another container with TextField CheckBox and the Button is also dependent on this TableA's validation so do I've to make separate methods for it or I can use the same method like this: `private void validateTable(GridPane table, GridPane tableA, Button button)` – Junaid May 31 '17 at 09:24
  • @Junaid If `TableA` is a `GridPane` and contains `TextFields`, you can absolutely use the same method for this table as well. – Yahya May 31 '17 at 14:53
  • Ok @Yahya but TableA is not in the same GridPane it's in different GridPane. So I tried to do like you're saying, but problem is that when all the entries of Table is validate it immediately enable the button **(Problem)** without checking TableA entries which is in different gridPane. So after all this when I click on TableA fields it'll again disable the button until TableA entries validate. But I want program should validate both Table and TableA **(both are in different GridPanes)** and then enable button not one by one. if you don't get what I'm trying to say I'll update my question. – Junaid May 31 '17 at 17:13
  • [link](https://stackoverflow.com/questions/44294321/javafx-how-to-apply-validation-on-two-gridpanes-nodes-simultaneously) @Yahya can you please check now. – Junaid May 31 '17 at 20:43