0

I have been trying to figure out how to deal with a FXML file in another FXML file. But I have an exception. :( This is my inner FXML (NewInside.fxml in view package):

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane prefHeight="305.0" prefWidth="360.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="controller.NewInsideController">
   <children>
      <Button fx:id="newBtn" layoutX="30.0" layoutY="202.0" mnemonicParsing="false" onAction="#btnClick" text="Button" />
      <TextField fx:id="newTxt" layoutX="30.0" layoutY="134.0" prefHeight="25.0" prefWidth="280.0" />
      <Label fx:id="newLbl" layoutX="30.0" layoutY="62.0" prefHeight="17.0" prefWidth="280.0" />
   </children>
</AnchorPane>

This is its controller (NewInsideController.java in controller package):

package controller;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class NewInsideController implements Initializable{

    @FXML public static Label newLbl;
    @FXML public static TextField newTxt;
    @FXML public static Button newBtn;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }

    @FXML
    private void btnClick(ActionEvent e){
        System.out.println("Button is clicked!");
        newLbl.setText(newTxt.getText());
    }

}

And this is outer FXML (New.fxml in view package):

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

<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane prefHeight="380.0" prefWidth="529.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="controller.NewController">
   <children>
      <fx:include source="NewInside.fxml" />
   </children>
</AnchorPane>

This is its controller (NewController.java in controller package):

package controller;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;

public class NewController implements Initializable {

    /*@FXML private Label newLbl;
    @FXML private Button newBtn;
    @FXML private TextField newTxt;
    */

    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }

    /*@FXML
    public void btnClick(ActionEvent e){
        newLbl.setText(newTxt.getText());
    }*/

}

Finally, this is the Main.java in application package:

package application;

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


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("/view/New.fxml"));
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

When I clicked the button, it gives me very long exception: "Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException.... ..... Caused by: java.lang.NullPointerException at controller.NewInsideController.btnClick(NewInsideController.java:27) ... 57 more"

Please help me! :(

Yin
  • 395
  • 4
  • 6
  • 16
  • 2
    I'm just a beginner but just a guess: remove the word static from them FXML variables? – Inge Feb 07 '15 at 09:08

1 Answers1

1

As @Inge suggests, using the static field is not the right way to do it. Also, instead of public, you should use private. If you need to expose your controls, use their properties.

You can read why here:

Just change this in your NewInsideController:

@FXML private Label newLbl;
@FXML private TextField newTxt;
@FXML private Button newBtn;

EDIT

If you want to have access to your controls from other classes, instead of adding getters/setters to expose directly the controls, it is better just exposing their properties.

For instance, to have access to the text of these controls you could add:

public StringProperty newLblText() { 
    return newLbl.textProperty();
}
public StringProperty newTxtText() { 
    return newTxt.textProperty();
}
public StringProperty newBtnText() { 
    return newBtn.textProperty();
}

This will allow you binding (or listening to changes), getting/setting the text property from any of them from the outside of your controller.

To get a valid instance of NewInsideController from NewController, follow this link. First modify New.fxml:

<fx:include fx:id="newInside" source="NewInside.fxml" />

and now you can get an instance, and add some listeners with those properties:

public class NewController implements Initializable {

    @FXML
    private Parent newInside;
    @FXML
    private NewInsideController newInsideController;

    @Override
    public void initialize(URL url, ResourceBundle rb) {

        newInsideController.newBtnText().addListener(
            (obs,s,s1)->System.out.println("nweBtn Text;" +s1));
        if(newInsideController.newLblText().get().isEmpty()){
            newInsideController.newLblText().set("Text for the textfield");
        }
        newInsideController.newTxtText().addListener(
           (obs,s,s1)->System.out.println("s1 "+s1));
    }    
}
Community
  • 1
  • 1
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • What do you mean by "If you need to expose your controls, use their properties"? Could you explain it to me? – Yin Feb 08 '15 at 02:27
  • Where should I place the last chunk of code into? Is it in controller class? – Yin Feb 10 '15 at 02:50
  • The intention of the properties added in NewInsideController is allowing some interaction from an outside class, providing the latter has a valid instance of the former. For instance, let's say that `NewController` needs to set/get some parameters to/from `NewInsideController`. I've edited the answer to clarify this. – José Pereda Feb 10 '15 at 08:29