-1

I have a JavaFX app that has the function to bring up a FileChooser. I just want to get the path of the file selected and set a Text Field to that path in the app. I need to re-use this function 4 times. Every time, I pass the TextField ID to the function it says it's null and then get the NullPointerException. I've tried several different solutions that said to create an initialize() for it, but that does not work either.

Multiple sources said to initialize the text field objects such as:

TextField global_dataset_1 = new TextField(); 

Then some sources said don't do that and to just refer to the .fxml IDs; which is what I thought originally to do... I tried the first one above by declaring it in the beginning and wrapping it into an @Override initialize(). Neither worked. I have the IDs set in the .fxml file.

I've also looked at: http://tutorials.jenkov.com/javafx/filechooser.html and https://examples.javacodegeeks.com/desktop-java/javafx/fxml/javafx-fxml-controller-example/ and https://github.com/mwilchek/Restaurant-Gift-Card-Lookup-App/blob/master/src/controller/NewAccountController.java

Below is what I have:

Main.java

package views;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;

public class Main extends Application {

    public static Stage primaryStage = new Stage();

    @Override
    public void start(Stage primaryStage) throws IOException{
        Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
        primaryStage.setTitle("Configuration Manager");
        primaryStage.setScene(new Scene(root, 1100, 700));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

FxmlController.java

package controller;

import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Accordion;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import java.io.File;

import static views.Main.primaryStage;


public class FxmlController {

    @FXML
    Accordion mainList;
    @FXML
    AnchorPane AnchorPane1;

    //TextFields are all set to NULL ERROR
    @FXML
    TextField global_dataset_1;
    @FXML
    TextField global_dataset_2;
    @FXML
    TextField global_dataset_3;
    @FXML
    TextField global_dataset_4;

    public void fileChooser1() {
        fileChooser(global_dataset_1);
    }

    public void fileChooser2() {
        fileChooser(global_dataset_2);
    }

    public void fileChooser3() {
        fileChooser(global_dataset_3);
    }

    public void fileChooser4() {
        fileChooser(global_dataset_4);
    }

    public FxmlController(){

    }

    // Error: For some reason won't pass TextField Object here...
    public void fileChooser(TextField field) {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("Select Global Dataset");
        fileChooser.getExtensionFilters().addAll(
            new FileChooser.ExtensionFilter("CSV Files", "*.csv"),
            new FileChooser.ExtensionFilter("All Files", "*.*"));
        File selectedFile = fileChooser.showOpenDialog(primaryStage);
        field.appendText(selectedFile.getPath());
    }

Main.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.scene.control.Accordion?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.SeparatorMenuItem?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>

<VBox prefHeight="700.0" prefWidth="1100.0" style="-fx-background-color: white;" stylesheets="@bootstrap3.css" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.FxmlController">
  <children>
    <MenuBar style="-fx-background-color: #2176ff;" styleClass="context-menu" stylesheets="@bootstrap3.css" >
      <menus>
        <Menu mnemonicParsing="false" styleClass="menu-item" text="File">
          <items>
            <MenuItem mnemonicParsing="false" text="New" />
            <MenuItem mnemonicParsing="false" text="Open…" />
            <Menu mnemonicParsing="false" text="Open Recent" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Close" />
            <MenuItem mnemonicParsing="false" text="Save" />
            <MenuItem mnemonicParsing="false" text="Save As…" />
            <MenuItem mnemonicParsing="false" text="Revert" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Preferences…" />
            <SeparatorMenuItem mnemonicParsing="false" />
            <MenuItem mnemonicParsing="false" text="Quit" />
          </items>
        </Menu>
        <Menu mnemonicParsing="false" text="Help">
          <items>
            <MenuItem mnemonicParsing="false" text="About" />
          </items>
        </Menu>
      </menus>
    </MenuBar>
      <SplitPane dividerPositions="0.5, 0.5, 0.5" prefHeight="659.0" prefWidth="1100.0" >
         <items>
            <Accordion id="mainList" prefWidth="384.0">
              <panes>
                <TitledPane animated="false" styleClass="primary" stylesheets="@bootstrap3.css" text="Global Configurations">
                  <content>
                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="52.0" prefWidth="393.0" >
                           <children>
                              <Label layoutX="14.0" layoutY="15.0" prefHeight="41.0" prefWidth="93.0" text="Year:">
                                 <font>
                                    <Font size="18.0" />
                                 </font>
                              </Label>
                               <ChoiceBox id="year" layoutX="405.0" layoutY="21.0" prefWidth="116.0" style="-fx-background-color: #2176ff;" styleClass="primary" stylesheets="@bootstrap3.css" value="2018">
                                   <items>
                                       <FXCollections fx:factory="observableArrayList">
                                           <String fx:value="2018" />
                                           <String fx:value="2019" />
                                           <String fx:value="2020" />
                                           <String fx:value="2021" />
                                           <String fx:value="2022" />
                                       </FXCollections>
                                   </items>
                               </ChoiceBox>
                              <Label layoutX="14.0" layoutY="70.0" text="Run Name:">
                                 <font>
                                    <Font size="18.0" />
                                 </font>
                              </Label>
                              <Separator layoutX="-3.0" layoutY="54.0" prefHeight="13.0" prefWidth="537.0" />
                              <TextField id="run_name" layoutX="298.0" layoutY="68.0" prefHeight="32.0" prefWidth="225.0" promptText="Run Name" style="-fx-background-color: #2176ff; -fx-text-fill: white;" styleClass="primary" stylesheets="@bootstrap3.css">
                                 <font>
                                    <Font size="14.0" />
                                 </font>
                              </TextField>
                              <Separator layoutX="-2.0" layoutY="109.0" prefHeight="13.0" prefWidth="537.0" />
                              <Label layoutX="14.0" layoutY="122.0" prefHeight="35.0" prefWidth="140.0" text="Global Datasets" underline="true">
                                 <font>
                                    <Font name="Century" size="18.0" />
                                 </font>
                              </Label>
                              <Label layoutX="14.0" layoutY="160.0" text="Dataset 1: ">
                                 <font>
                                    <Font size="18.0" />
                                 </font>
                              </Label>
                              <Label layoutX="14.0" layoutY="192.0" text="Dataset 2: ">
                                 <font>
                                    <Font size="18.0" />
                                 </font>
                              </Label>
                              <Label layoutX="14.0" layoutY="227.0" text="Dataset 3: ">
                                 <font>
                                    <Font size="18.0" />
                                 </font>
                              </Label>
                              <Label layoutX="14.0" layoutY="263.0" text="Dataset 4: ">
                                 <font>
                                    <Font size="18.0" />
                                 </font>
                              </Label>
                              <TextField id="global_dataset_1" cache="true" layoutX="111.0" layoutY="158.0" prefWidth="379.0" promptText="Path to File" styleClass="primary" stylesheets="@bootstrap3.css" />
                              <Button layoutX="497.0" layoutY="157.0" mnemonicParsing="false" onMouseClicked="#fileChooser1" styleClass="primary" stylesheets="@bootstrap3.css" text="..." />
                              <TextField id="global_dataset_2" cache="true" layoutX="111.0" layoutY="191.0" prefWidth="379.0" promptText="Path to File" styleClass="primary" stylesheets="@bootstrap3.css" />
                              <TextField id="global_dataset_3" cache="true" layoutX="111.0" layoutY="227.0" prefWidth="379.0" promptText="Path to File" styleClass="primary" stylesheets="@bootstrap3.css" />
                              <TextField id="global_dataset_4" cache="true" layoutX="110.0" layoutY="263.0" prefWidth="379.0" promptText="Path to File" styleClass="primary" stylesheets="@bootstrap3.css" />
                              <Button layoutX="497.0" layoutY="192.0" mnemonicParsing="false" styleClass="primary" stylesheets="@bootstrap3.css" text="..." />
                              <Button layoutX="497.0" layoutY="226.0" mnemonicParsing="false" styleClass="primary" stylesheets="@bootstrap3.css" text="..." />
                              <Button layoutX="497.0" layoutY="262.0" mnemonicParsing="false" styleClass="primary" stylesheets="@bootstrap3.css" text="..." />
                           </children></AnchorPane>
                  </content>
                </TitledPane>
                <TitledPane animated="false" styleClass="primary" stylesheets="@bootstrap3.css" text="Annual 1">
                  <content>
                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
                  </content>
                </TitledPane>
                <TitledPane animated="false" styleClass="primary" stylesheets="@bootstrap3.css" text="Annual 2">
                  <content>
                    <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
                  </content>
                </TitledPane>
              </panes>
            </Accordion>
            <AnchorPane id="AnchorPane1" prefHeight="200.0" prefWidth="200.0" visible="true" />
            <AnchorPane id="AnchorPane2" prefHeight="200.0" prefWidth="200.0" visible="false" />
            <AnchorPane id="AnchorPane3" prefHeight="200.0" prefWidth="200.0" visible="false" />
         </items>
      </SplitPane>
  </children>
</VBox>

Any help would be greatly appreciated!

fabian
  • 80,457
  • 12
  • 86
  • 114
  • having an array of text fields is better/more felxible than having 4 seperate text fields, all of which have their own fileChooser method. just pass the index of your desired textfield object you want to set to the method so you can do something like `fileChooser(int index){fileChooser(textFieldArray[index])}` – RAZ_Muh_Taz Jun 14 '18 at 18:01
  • I can implement that as a workaround for the fileChooser, however, the fields are still set to null. I don't understand why when I refer to them in the controller and have them IDed in the .fxml file.... :-/ – Programming_Noob Jun 14 '18 at 18:13

1 Answers1

2

You have errors with how you've built your FXML file. Within your tags, you need to set the node's ID using fx:id but you only do id.

Your TextFields should be changed to:

<TextField fx:id="global_dataset_1" ...

A couple side notes:

You may want to use field.setText() instead of appendText() unless your intention is to allow them to select multiple files and list them all in the same text field.

Also, in your controller class, you should declare your nodes as private:

@FXML
private TextField global_dataset_1;

Lastly, sharing your primaryStage as a public field is not necessary (or recommended). Another way to open the FileChooser centered would be to get the current Window from any node declared in your controller:

File selectedFile = fileChooser.showOpenDialog(
                global_dataset_1.getScene().getWindow());
Zephyr
  • 9,885
  • 4
  • 28
  • 63
  • It's not even ***the*** primary stage that's shared. It's some other `Stage` that is never used. except for passing it to the `showOpenDialog` method. – fabian Jun 14 '18 at 18:19
  • Thanks for the suggestion Zephyr, unfortunately, when I enter that my IDE says "Cannot set javafx.scene.control.TextField to field 'global_dataset_1' :-/ – Programming_Noob Jun 14 '18 at 18:21
  • @fabian Oh right. I missed the import and misread the usage. `primaryStage` is still declared `public`, though. – Zephyr Jun 14 '18 at 18:21
  • @Programming_Noob either your IDE is wrong (unlikely) or something else has gotten messed up when you made your change. If all you do is change the `id` to `fx:id` for each `TextField` in your FXML, you should not have a problem. – Zephyr Jun 14 '18 at 18:24
  • Yea, I'm not getting any error at that point when it runs. I get the NullPointException Error on the TextField. – Programming_Noob Jun 14 '18 at 18:25
  • @Zephyr Hmm I'm using IntelliJ. And I just entered your suggestion under the 'Text' tab for the .fxml file. I can try using Scenebuilder instead... – Programming_Noob Jun 14 '18 at 18:26
  • The console keeps giving me this error: java.lang.IllegalArgumentException: Can not set javafx.beans.property.SimpleObjectProperty field controller.FxmlController.global_dataset_1 to javafx.scene.control.TextField – Programming_Noob Jun 14 '18 at 18:33
  • Then you've changed something else. The code you posted imports `SimpleObjectProperty` but never uses it. – Zephyr Jun 14 '18 at 18:34
  • I'm a noob. I see what I did. You were right @Zephyr. Thanks so much for pointing out a silly mistake. – Programming_Noob Jun 14 '18 at 18:39
  • Hey, been there, done that! :) Glad I could help! – Zephyr Jun 14 '18 at 18:41