JavaFX #FXML binding from within Java code - javafx

I have a custom JavaFX component extending the Tab class:
public class MyTab extends Tab implements Initializable {
#FXML private TextField myInput;
private final MyDTO dto;
public MyTab(MyDTO dto) {
super();
this.dto = dto;
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/my-tab.xml"));
fxmlLoader.setResources(MSG.getResourceBundle());
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
}
catch (IOException exception) {
throw new RuntimeException(exception);
}
}
#Override
public void initialize(URL url, ResourceBundle res) {
setText("My Tab");
myInput.setText(dto.getValue()); // !!!
}
}
With the FXML:
<fx:root type="javafx.scene.control.Tab" xmlns:fx="http://javafx.com/fxml">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<Label id="myLabel" layoutX="14.0" layoutY="14.0" text="Text:" />
<TextField id="myInput" layoutX="162.0" layoutY="10.0" prefHeight="25.0" prefWidth="300.0" />
</children>
</AnchorPane>
</content>
</fx:root>
I need to create this objects (custom tabs) dynamically from the java code:
final MyTab myTab = new MyTab(new MyDTO(...));
tabPane.getTabs().add(myTab);
When I use it like this, the #FXML binding doesn't work and the line
myInput.setText(dto.getValue());
throws NullPointerException. When the line with the setting of the text from the code is commented, the input is showned, so the problem is only in the binding.
I am using JavaFX 2 for Java 1.7
Thank you for any idea!

Solution is very easy, I just overlooked the mistake in the FXML code:
Should be
<TextField fx:id="myInput" ...
instead of
<TextField id="myInput" ...

Related

Can not create clearable textField using ControlsFX

I have created TextField using Scene Builder but when I tried it with KeyEvent but it doesn't work.
public class FXMLDocumentController implements Initializable {
#FXML
private TextField searchPatient;
#FXML
void keyTyped(KeyEvent event) {
this.searchPatient = TextFields.createClearableTextField();
}
}
Here is main class:
public class MyFX extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is FXMLDocument.fxml:
<AnchorPane id="AnchorPane" prefHeight="597.0" prefWidth="726.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.homeo.home.FXMLDocumentController">
<children>
<TextField fx:id="searchPatient" layoutX="14.0" layoutY="109.0" onKeyTyped="#keyTyped" prefHeight="26.0" prefWidth="372.0" promptText="Search" />
</children>
</AnchorPane>
I don't want to relay on code for designing that's why I want to use ControlsFX with IDE generated code.
I'm expecting textfield must show clear icon as I enter few text in a textfield that's called clearableTextFieldf.
But with the all code above doesn't work as I expected.
It does not create ClearableTextField
You're creating the TextField in the KEY_TYPED event handler of a conventional TextField placed in your scene. The newly created TextField is never added to any scene though.
You could use the fx:factory attribute to use the static method for creating the TextField that FXMLLoader adds to the scene:
...
<?import org.controlsfx.control.textfield.TextFields?>
<AnchorPane id="AnchorPane" prefHeight="597.0" prefWidth="726.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.homeo.home.FXMLDocumentController">
<children>
<TextFields fx:factory="createClearableTextField" fx:id="searchPatient" layoutX="14.0" layoutY="109.0" prefHeight="26.0" prefWidth="372.0" promptText="Search" />
</children>
</AnchorPane>
You could alternatively use the initialize method to create&add your TextField instead:
public class FXMLDocumentController implements Initializable {
...
#FXML
private AnchorPane anchorPane;
#Override
public void initialize(URL location, Resources resources)
searchPatient = TextFields.createClearableTextField();
searchPatient.setLayoutX(14.0);
searchPatient.setLayoutY(109.0);
searchPatient.setPrefSize(372.0, 26.0);
searchPatient.setPromptText("Search");
anchorPane.getChildren().add(searchPatient);
...
}
}
<AnchorPane fx:id="anchorPane" id="AnchorPane" prefHeight="597.0" prefWidth="726.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.homeo.home.FXMLDocumentController" />

JavaFX FXML Text Component Controller not updating

I am having a difficult time getting an FXML Text component to update after being dynamically created using FXMLLoader. This supposedly simple program asks a user to input the number of die to roll, creates new dice based upon this input, rolls them with a random number generator, sums these values, and provides a button to re-roll any of the die if they want. Each die is added to the main window (using FXMLoader and a custom die component) showing its index, the value of the roll, and a button to push if you want to re-roll a particular die. The values of the die present and update great. The re-roll buttons work great. However, for some reason, the index for each die does not show in the window - not even initially. The Text component in question (the one that won't show any values) is identified as fx:id="number" in the custom FXML document and as #FXML private Text number in the custom controller.
I'm assuming the issue has to do with the Text component's getters and setters and I'm lucky that the Text component named value works. However, if this is the case, the proper names have alluded me as I have checked many alternatives involving Property in the name, etc.
Note: I understand the sum of the die values won't be updated when the re-roll buttons are pushed. I only care about the die values being updated for now.
The main FXML window for the program is:
<GridPane fx:id="mainPane"
styleClass="mainFxmlClass"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.lgdor.dieSim.MainWindowController"
xmlns="http://javafx.com/javafx/8.0_40"
prefHeight="800.0"
prefWidth="800.0"
hgap="10"
vgap="10" >
<stylesheets>
<URL value="#mainwindow.css"/>
</stylesheets>
<padding>
<Insets top="25" right="25" bottom="10" left="25"/>
</padding>
<Text fx:id="welcomeText" text="Welcome to DieSim"
GridPane.columnIndex="0"
GridPane.rowIndex="0"
GridPane.columnSpan="5"/>
<Label fx:id="spinnerText"
text="Please input the number of dice you want to roll:"
GridPane.columnIndex="0"
GridPane.rowIndex="1"
GridPane.columnSpan="2"/>
<Spinner fx:id="spinner" editable="true"
GridPane.columnIndex="3"
GridPane.rowIndex="1" />
<HBox fx:id="buttonBox"
spacing="10"
alignment="center"
GridPane.columnIndex="5"
GridPane.rowIndex="1">
<Button text="Roll Dice" onAction="#createDice"/>
</HBox>
<Label fx:id="dieNumberText" text="The number of dice is: "
GridPane.columnIndex="0"
GridPane.rowIndex="2"
GridPane.columnSpan="2"/>
<Text fx:id="dieNumber" GridPane.columnIndex="3" GridPane.rowIndex="2" />
<Label fx:id="dieSumText" text="The sum of the dice is: "
GridPane.columnIndex="0"
GridPane.rowIndex="3"
GridPane.columnSpan="2"/>
<Text fx:id="sumNumber" GridPane.columnIndex="3" GridPane.rowIndex="3" />
<VBox fx:id="rowBox"
spacing="10"
alignment="center"
GridPane.columnIndex="0"
GridPane.rowIndex="4"
GridPane.columnSpan="5" >
<Text text="This is where the list of dice goes..."/>
</VBox>
The main controller for this is:
public class MainWindowController implements Initializable {
#FXML
Label spinnerText;
#FXML
Spinner spinner;
#FXML
HBox buttonBox;
#FXML
Text dieNumber;
#FXML
Text sumNumber;
#FXML
int numberOfDice = 0;
#FXML
int sumOfDice = 0;
#FXML
VBox rowBox;
private List<Die> dieList = new ArrayList<>();
private List<GridPane> rowList = new ArrayList<>();
public void createDice() throws IOException{
numberOfDice = (int)spinner.getValue();
for (int i=0;i<numberOfDice;i++){
Die die = new Die(i);
die.roll(null);
sumOfDice = sumOfDice + Integer.parseInt(die.getValue());
dieList.add(die);
rowBox.getChildren().add(die);
}
dieNumber.setText(String.valueOf(numberOfDice));
sumNumber.setText(String.valueOf(sumOfDice));
spinner.setVisible(false);
spinnerText.setVisible(false);
buttonBox.setVisible(false);
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
spinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 20));
dieNumber.setText("0");
sumNumber.setText(String.valueOf("0"));
spinnerText.setVisible(true);
spinner.setVisible(true);
buttonBox.setVisible(true);
}
}
The die custom component that is dynamically created in the for loop above is (problem component is fx:id="number"):
<fx:root type="javafx.scene.layout.GridPane"
xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0_40" >
<HBox spacing="30" alignment="center" GridPane.columnIndex="0" GridPane.rowIndex="0" >
<Text fx:id="number" /><!--This is the problem component-->
<Text fx:id="value"/>
<Button fx:id="dieButton" text="Roll Dice" onAction="#roll" />
</HBox>
</fx:root>
and its dynamically created controller is:
public class Die extends GridPane implements Initializable {
#FXML
private Text number;//This is the problem component ...
public String getNumber() {
return numberTextProperty().get();
}
public void setNumber(String number) {
numberTextProperty().set(number);
}
public StringProperty numberTextProperty() {
return number.textProperty();
}
#FXML
private Text value;
public String getValue() {
return valueTextProperty().get();
}
public void setValue(String value) {
valueTextProperty().set(value);
}
public StringProperty valueTextProperty() {
return value.textProperty();
}
public Die(int i) throws IOException{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("DieGUI.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
this.number = new Text(String.valueOf(i));
this.setNumber(String.valueOf(1));
this.value = new Text();
this.setValue(String.valueOf(1));
fxmlLoader.load();
}
#FXML
public void roll(ActionEvent event){
this.setValue(String.valueOf((int)(6*Math.random()+1)));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
The problem is you create a new Text instance, instead of configuring the one already created in the FXML:
this.number = new Text(String.valueOf(i));
The whole purpose of an #FXML annotation is to indicate that the field is initialized by the FXMLLoader, so it is always a mistake to initialize fields annotated #FXML.
Note here that these fields are initialized by the FXMLLoader during the call to load(), so you need to call load() before you configure them in any way.
So your constructor should look like:
public Die(int i) throws IOException{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("DieGUI.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
fxmlLoader.load();
// these don't seem to do anything useful?
// this.setNumber(String.valueOf(1));
// this.setValue(String.valueOf(1));
this.number.setText(String.valueOf(i));
this.value.setText(String.valueOf(i));
}

StringProperty and Switch Statement Javafx

My brain is about to explode. I am pretty new to java, so far self-taught but I really want to get this code to work. I am also not the best at making sense of the java docs.
I am trying to create a gameloop where the user can freely traverse the switch statement by clicking the buttons.
However, previously, when my gameloop method was called, it ran the code with the current settings without letting me use the buttons to traverse the switch statement.
I think this was because my "choice" variable did not have an action listener attached to it (because a listener will make the program sit on the item, waiting for something to happen?). Unfortunately, my choice variable was a String variable and you cannot attach an actionlistener to a String variable.
Now I tried using the StringProperty class to create a string object to be able to attach an action listener to it.
But now I don't even know how to use StringProperty with switch statement and I just feel like I am on the wrong track.
Does it make sense what I am trying to accomplish?
Can anybody help me create a gameloop where the user can freely traverse the switch statement by clicking the buttons?
♥♥♥!
Here is my code:
Controller:
package sample;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
import static java.lang.System.out;
public class Controller implements Initializable {
public String question;
public StringProperty choice = new SimpleStringProperty(this, "choice", "");
// this code starts when the XML file is loaded.
#Override
public void initialize(URL location, ResourceBundle resources) {
question = "0";
gameloop();
} public String getChoice() {
return choice.get();
}
public StringProperty choiceProperty() {
return choice;
}
public void setChoice(String choice) {
this.choice.set(choice);
}
// public String choice = "";
public Controller() {
}
// Here are the buttons trying to add an actionlistener to the choice object.
// Maybe wrong path to go
#FXML
public void button1() {
choiceProperty().addListener((v, oldValue, newValue) ->{setChoice("c1");});
}
#FXML
public void button2() {
choiceProperty().addListener((v, oldValue, newValue) ->{setChoice("c2");});
out.println(choice); //think this prints out where it is stored in the memory
}
#FXML
public void button3() {
choiceProperty().addListener((v, oldValue, newValue) ->{setChoice("c3");});
out.println(choice);
}
//this block of code does not work because of the switch (choice) statement.
//Where "c1","c2","c3" have incompatible a type error.
public void gameloop() {
switch (question) {
case "0":
switch (choice) {
case "c1":
out.println("you chose 1");
question = "1";
break;
case "c2":
out.println("you chose 2");
break;
case "c3":
out.println("you chose 3");
break;
}
case "1":
switch (choice) {
case "c1":
out.println("you chose a");
question = "0";
break;
case "c2":
out.println("you chose b");
break;
case "c3":
out.println("you chose c");
break;
}
}
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<BorderPane layoutX="163.0" layoutY="82.0" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: black;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<bottom>
<GridPane BorderPane.alignment="CENTER">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button fx:id="button1" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button1" onMouseClicked="#button1" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" />
<Button fx:id="button2" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button2" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="1" />
<Button fx:id="button3" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button3" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="2" />
</children>
</GridPane>
</bottom>
<center>
<TextArea fx:id="textArea" editable="false" prefHeight="200.0" prefWidth="200.0" promptText="..." style="-fx-border-color: grey;" wrapText="true" BorderPane.alignment="CENTER" />
</center>
<top>
<Label fx:id="label" text="Chapter 1" textFill="WHITE" BorderPane.alignment="CENTER" />
</top>
<left>
<ImageView fx:id="image1" fitHeight="75.0" fitWidth="75.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
</left>
<right>
<ImageView fx:id="image2" fitHeight="75.0" fitWidth="75.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
</right></BorderPane>
</children>
</AnchorPane>
Main:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 800, 500));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Registering listeners in the button event handlers is nonsense. Using listeners to modify the property they listen to is even worse. Passing a property as argument for a switch will not even compile.
In this case the smallest modification to make this work would be registering a single listener in the initialize method, calling the gameloop method from this listener and using the value stored in the property as argument for the switch.
#Override
public void initialize(URL location, ResourceBundle resources) {
question = "0";
gameloop();
choiceProperty().addListener((observable, oldValue, newValue) -> gameloop());
}
...
#FXML
private void button1() {
setChoice("c1");
}
...
public void gameloop() {
switch (question) {
case "0":
switch (choice.get()) {
...
}
...
}
}
Note however you'll get a LOT of repeated code this way. Better come up with a data structure to store the game data instead of hardcoding everything.
E.g.
public class GameData {
private final ReadOnlyObjectWrapper<Question> question;
public ReadOnlyObjectProperty<Question> questionProperty() {
return question.getReadOnlyProperty();
}
public Question getQuestion() {
return question.get();
}
private final Map<Integer, Question> questions;
public Map<Integer, Question> getQuestions() {
return questions;
}
public GameData(int initialQuestion, Map<Integer, Question> questions) {
this.questions = questions;
this.question = new ReadOnlyObjectWrapper(questions.get(initialQuestion));
}
public void activateChoice(Choice choice) {
question.set(questions.get(choice.getNextQuestion()));
}
}
public class Question {
private final String text;
public String getText() {
return text;
}
public Question(String text, Choice... choices) {
this.text = text;
this.choices = Collections.unmodifiableList(new ArrayList(Arrays.asList(choices)));
}
private final List<Choice> choices;
public List<Choice> getChoices() {
return choices;
}
}
public class Choice {
private final String text;
private final int nextQuestion;
public Choice(int nextQuestion, String text) {
this.text = text;
this.nextQuestion = nextQuestion;
}
public String getText() {
return text;
}
public int getNextQuestion() {
return nextQuestion;
}
}
public class Controller {
private GameData gameData;
#FXML
private Button button1, button2, button3;
private Button[] buttons;
#FXML
private void initialize() {
buttons = new Button[]{button1, button2, button3};
}
public void setGameData(GameData gameData) {
this.gameData = gameData;
gameData.questionProperty().addListener((observable, oldValue, newValue) -> setQuestion(newValue));
setQuestion(gameData.getQuestion());
}
private void setQuestion(Question question) {
System.out.println(question.getText());
for (Button b : buttons) {
b.setVisible(false);
}
for (int i = 0, max = Math.min(buttons.length, question.getChoices().size()); i < max; i++) {
Button b = buttons[i];
Choice c = question.getChoices().get(i);
b.setUserData(c);
b.setText(c.getText());
b.setVisible(true);
}
}
#FXML
private void button(ActionEvent evt) {
Node source = (Node) evt.getSource();
Choice choice = (Choice) source.getUserData();
out.println("You've chosen " + choice.getText());
gameData.activateChoice(choice);
}
}
...
<Button fx:id="button1" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" />
<Button fx:id="button2" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="1" />
<Button fx:id="button3" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#button" style="-fx-background-color: black; -fx-border-color: grey;" text="Button" textFill="WHITE" GridPane.rowIndex="2" />
...
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
Map<Integer, Question> questions = new HashMap<>();
questions.put(0, new Question("0",
new Choice(1, "1"),
new Choice(0, "2"),
new Choice(0, "3")
));
questions.put(1, new Question("1",
new Choice(0, "a"),
new Choice(1, "b"),
new Choice(1, "c")
));
GameData data = new GameData(0, questions);
loader.<Controller>getController().setGameData(data);

JavaFX, why is TableColumn null?

As you will see I am a massive noob when it comes to Java and Javafx. I have spent a lot of time reading around (various forum posts, and tuts) and trying to figure out myself where I am getting this issue but it has come to the point for me to post for feedback from someone who knows their business.
When replying, please could you take the time to also explain why something isn't working and some general pointers? Here is what I have so far (my FXML and my two classes) any pointers would be fantastic!!
My FXML;
<Pane id="myScene" fx:id="myScene" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sample.TestController">
<children>
<TableView id="employeesTable" fx:id="employeesTable" layoutX="131.0" layoutY="64.0" prefHeight="200.0" prefWidth="360.0">
<columns>
<TableColumn id="colFirstName" fx:id="colFirstName" prefWidth="75.0" text="First Name" />
<TableColumn id="colLastName" fx:id="colLastName" prefWidth="75.0" text="Last Name" />
<TableColumn id="colEmail" fx:id="colEmail" prefWidth="75.0" text="email" />
</columns>
</TableView>
</children>
</Pane>
Now the Employee class I have;
public class Employee {
private StringProperty firstName;
private StringProperty lastName;
private StringProperty email;
public Employee(String a, String b, String c) {
this.firstName = new SimpleStringProperty(a);
this.lastName = new SimpleStringProperty(b);
this.email = new SimpleStringProperty(c);
}
public Employee() {
}
public String getFirstName() {
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getEmail() {
return email.get();
}
public StringProperty emailProperty() {
return email;
}
public void setEmail(String email) {
this.email.set(email);
}
}
And finally the class for my test controller is
public class TestController {
public Label login;
public TextField loginUserName;
public PasswordField loginPassword;
public TextField testOutput;
#FXML TableView<Employee> employeesTable;
#FXML TableColumn<Employee, String> colFirstName;
#FXML TableColumn<Employee, String> colLastName;
#FXML TableColumn<Employee, String> colEmail;
#FXML Pane myScene;
//public javafx.scene.control.TableView employeesTable;
private ObservableList<Employee> myData;
private MainController MainController;
public void loadEmployeeForm(ActionEvent actionEvent) throws IOException, SQLException, ClassNotFoundException {
myData = FXCollections.observableArrayList(DBCON.getEmployees());
System.out.println(myData.size());
Parent root = FXMLLoader.load(getClass().getResource("frmEmployees.fxml"));
Scene myScene = new Scene( root );
sample.MainController.setScene(myScene);
colFirstName.setCellValueFactory(new PropertyValueFactory<Employee, String>("firstName"));
colLastName.setCellValueFactory(new PropertyValueFactory<Employee, String>("lastName"));
colEmail.setCellValueFactory(new PropertyValueFactory<Employee, String>("email"));
employeesTable.setItems(null);
employeesTable.setItems(myData);
employeesTable.setVisible(true);
}
I get a null pointer exception when I go to set colFirstName to the property value factory which make me think I haven't initialized something somewhere but I am utterly clueless on how to go about adding that.
If I add in lines such as;
TableColumn colFirstName = new TableColumn("firstName");
for each of my columns and the tablename it works (ie it doesn't throw a load of error messages at me) that way but then I don't get any data loading into the tableview because I think that's me creating a new tableView not using the one generated from the FXML?
I have a feeling it will be very simple, but as I said I am a massive noob and any points would be much obliged.
Thanks
Mark
Update 1;
The method for load employee form is called from a button on myMain.fxml;
<GridPane alignment="CENTER" hgap="10" prefHeight="300" prefWidth="300" vgap="10" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8"
fx:controller="sample.TestController" stylesheets="/sample/myFirst.css">
<children>
<Button onAction="#login" text="Login" GridPane.halignment="CENTER" GridPane.rowIndex="5" GridPane.valignment="CENTER" />
<Button text="GoEmployees" onAction="#loadEmployeeForm" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
<Label fx:id="login" GridPane.rowIndex="1" />
<Label text="UserName" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<Label text="Password" GridPane.columnIndex="0" GridPane.rowIndex="2" />
<TextField fx:id="loginUserName" GridPane.rowIndex="1" GridPane.columnIndex="1" />
<PasswordField fx:id="loginPassword" GridPane.rowIndex="2" GridPane.columnIndex="1" blendMode="OVERLAY" />
<TextField fx:id="testOutput" GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="3" />
</children>
<columnConstraints>
<ColumnConstraints prefWidth="125.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints prefHeight="50.0" />
</rowConstraints>
<padding>
<Insets bottom="10.0" left="9.0" right="10.0" top="10.0" />
</padding>
</GridPane>
Is having my testController control two different FXMLs a problem/a no go?
When the FXMLLoader tries to load an fxml file, it will create new instance of the controller class defined with fx:controller in fxml file. Then it creates and maps the #FXML annotated fields with fx:id components in fxml file. Finally, it calls the controller's initialize() method. You can get the instantiated controller with fxmlloader.getController() after fxmlloader.load().
According to this basic work flow, the pitfall in your code is:
myMain.fxml's controller is TestController, but myMain.fxml does not contain TableColumns with fx:id colFirstName etc. So these fields are null, when the myMain.fxml has been loaded. As a result, there will be NPE in loadEmployeeForm() while trying to use these fields.
Move the TableView and TableColumns to frmEmployees.fxml's controller, and configure them (setCellValueFactory, initial data etc.) in this controller's initialize() method.
You never use your Employee(String, String, String) constructor. This constructor initializes the firstName, lastName, and email. Otherwise, your references will point to nothing.

how to change parent fxml content from action perform on child fxml component?

I created JavaFX application and want to distribute complete functionality among different FXML with hierarchy and MVC structure.
At the start time RoolLayout.fxml is load which is parent FXML
RootLayout.fxml
<BorderPane prefHeight="400.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.nikunj.drclinic.controller.RootLayoutController">
<center>
<AnchorPane fx:id="dashboard" BorderPane.alignment="CENTER" />
</center>
<top>
<HBox BorderPane.alignment="CENTER">
<children>
<fx:include fx:id="mainMenu" source="MainMenuBar.fxml" />
</children>
</HBox>
</top>
</BorderPane>
For this used controller is RootLayoutController.java
public class RootLayoutController {
#FXML
private MainMenuBarController mainMenuBarController;
#FXML
private AnchorPane dashboard;
#FXML
private void initialize() {
// Initialize the person table with the two columns.
}
}
From inside this MainMenuBar.fxml file is also loaded which is child fxml file
MainMenuBar.fxml
<AnchorPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.nikunj.drclinic.controller.MainMenuBarController">
<children>
<MenuBar layoutY="2.0" prefWidth="1000.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" onAction="#closeApplication" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Patient Detail">
<items>
<MenuItem fx:id="addPatiendMenuItem" mnemonicParsing="false" onAction="#addPatient" text="Add Patient" />
<MenuItem mnemonicParsing="false" text="Find Patient" />
</items>
</Menu>
</menus>
</MenuBar>
</children>
</AnchorPane>
controller file for this MainMenuBar.fxml is MainMenuBarController.java
public class MainMenuBarController {
#FXML
private MenuItem addPatiendMenuItem;
#FXML
private MenuItem findPatientMenuItem;
#FXML
public void closeApplication(){
System.exit(0);
}
#FXML
public void addPatient(ActionEvent event){
}
}
Now on selection of menu item addPatiendMenuItem from controller addPatient(ActionEvent event) method is called.
From this method how can i change the AnchorPane fx:id = "dashboard" which is component of the parent fxml (RootLayout.fxml) file.
Suppose i want to load content of third fxml (i.e. Dashboard.fxml) in this AnchorPane, how can i do that?
I spend lots of hours to find, how to change parent controller component from the action performed on child controller component?
Create a property in the MainMenuBarController that represents the state you are changing ("view state"). Making this make sense depends on knowing a bit more about your application, but you might do something like
public class MainMenuBarController {
private final BooleanProperty addPatientRequested = new SimpleBooleanProperty();
public BooleanProperty addPatientRequestedProperty() {
return addPatientRequested ;
}
public final boolean isAddPatientRequested() {
return addPatientRequestedProperty().get();
}
public final boolean setAddPatientReqested(boolean requested) {
addPatientReqestedProperty().set(requested);
}
#FMXL
private void addPatient(ActionEvent event) {
setAddPatientRequested(true);
}
}
Then in the "parent" controller do
public class RootLayoutController {
#FXML
private MainMenuBarController mainMenuBarController;
#FXML
private AnchorPane dashboard;
#FXML
private void initialize() {
// Initialize the person table with the two columns.
mainMenuBarController.addPatientRequestedProperty().addListener((obs, wasRequested, isNowRequested) -> {
if (isNowRequested) {
// code to execute...
}
});
}
}
Depending on your application logic, you might want a different property, e.g. in the MainMenuBarController define
ObjectProperty<Node> display = new SimpleObjectProperty<>();
which would store the node the RootLayoutController is supposed to display. The structure would be similar, set that property in the addPatient handler method and listen to it in the RootLayoutController

Resources