Reputation: 23
I'm still new in developing my project in Java can you pin point what am I doing wrong in passing the String to another controller? Below is my solution. I'm trying to pass the value of String purchaseCode
from my first controller(DashboardController.java
) to another controller to either use it on a query or just simply set it to text. But I'm getting null as a result when I call it to my another controller(InfoSalesController.java
).
DashboardController.java
...
SalesController sales = getTableView().getItems().get(getIndex());
String purchaseCode = sales.getPurchaseCol();
Hyperlink link = new Hyperlink(purchaseCode);
link.setOnAction(event->{
try{
Stage stage = new Stage();
FXMLLoader loader = new
FXMLLoader(getClass().getResource("/store/sales/infoSales.fxml"));
Parent pane = (Parent) loader.load();
InfoSalesController is = loader.getController();
is.setPurchaseSales(purchaseCode);
stage.setScene(new Scene(pane));
stage.setResizable(false);
stage.initModality(Modality.APPLICATION_MODAL);
stage.show();
}catch(IOException e){
e.printStackTrace();
}
});
setGraphic(link);
...
InfoSalesController.java
public String getPurchaseSales() {
return purchaseSales;
}
public void setPurchaseSales(String purchaseSales) {
this.purchaseSales = purchaseSales;
}
@Override
public void initialize(URL url, ResourceBundle rb) {
purchaseTxt.setText(purchaseSales);
}
Upvotes: 2
Views: 7137
Reputation: 159281
This question is really a duplicate of:
But this question raises an interesting side question in: "how does the Platform.runLater()
help the program to work?", to which I'll post an answer here as an explanation.
First, let's look at the code from the passing parameters answer and explain how that works:
public Stage showCustomerDialog(Customer customer) {
FXMLLoader loader = new FXMLLoader(
getClass().getResource(
"customerDialog.fxml"
)
);
Stage stage = new Stage(StageStyle.DECORATED);
stage.setScene(
new Scene(loader.load())
);
CustomerDialogController controller = loader.getController();
controller.initData(customer);
stage.show();
return stage;
}
...
class CustomerDialogController {
@FXML private Label customerName;
void initialize() {}
void initData(Customer customer) {
customerName.setText(customer.getName());
}
}
The key difference here is that the initialize()
statement in the CustomerDialogController is empty, and a separate initData()
function has been added. The initialize()
statement is called implicitly by the FXMLLoader at the time that FXML is loaded. At that time, the rest of the data which is actually required to initialize the view (the customer data) isn't available because it hasn't yet been passed to the new controller. The initData(Customer customer)
call is made explicitly after the load and contains the customer data as a parameter, so that it can be used to initialize the view.
Now let's look at the code in your question and what Platform.runLater
does to it.
You have this code in your calling controller:
FXMLLoader loader = new
FXMLLoader(getClass().getResource("/store/sales/infoSales.fxml"));
Parent pane = (Parent) loader.load();
InfoSalesController is = loader.getController();
is.setPurchaseSales(purchaseCode);
And this in your receiving controller:
public void setPurchaseSales(String purchaseSales) {
this.purchaseSales = purchaseSales;
}
@Override
public void initialize(URL url, ResourceBundle rb) {
purchaseTxt.setText(purchaseSales);
}
which doesn't work, because purchaseTxt is null when the initialize statement is invoked.
But if you do the following, then purchaseTxt is not null:
@Override
public void initialize(URL url, ResourceBundle rb) {
Platform.runLater(() -> {
purchaseTxt.setText(purchaseSales);
});
}
What wrapping the content of initialize()
in Platform.runLater()
does is to delay the processing of the initialize function until some undefined point in the future.
Likely the execution will occur at the next scene pulse. A pulse is based on an internal timer that JavaFX uses to process animations and layout changes and send events and callbacks to application code.
For example, the Platform.runLater()
code could be executed 1/60th of a second later, after:
So, effectively, Platform.runLater()
makes the initialize process an asynchronous call. The asynchronous logic is executed after all of your subsequent code has been executed. This means that your code to set the application data in the new controller will be executed before the asynchronous code in the initialize()
Platform.runLater()
block is executed to initialize the view elements managed by the new controller.
Both approaches are valid. I guess you could choose the one which is most clear to you and other readers. Personally, I wouldn't use Platform.runLater()
in this situation.
Upvotes: 3