Passing objects between controllers without initializing the main window - javafx

I have read this post about Passing Parameters JavaFX FXML. What I want to know is. Is it possible to pass an object without reinitializing the window? Ideally, I would like to have a popup window where the user enters their login information. Then when they press the login button, the login window goes away and the login info is passed back to the main window.

Display the popup with showAndWait(), which will block execution until the window is dismissed. Then you can just call a method (that you define) on the controller to retrieve the data you need. Something like:
FXMLLoader loader = new FXMLLoader(...);
Scene scene = new Scene(loader.load());
Stage loginPopup = new Stage();
loginPopup.setScene(scene);
LoginController loginController = loader.getController();
loginPopup.showAndWait();
MyLoginData data = loginController.getLoginData();
// process data...

Related

JavaFX correct way to open and close a Stage multiple times

Problem:
I am working on a client server desktop application using JavaFx, everything works fine except that I have found that when I open a new stage clicking a button on the Home Page, the new Stage loads data only the first time I open it. The initialize method of the new stage sends a request to server and receives back an object whose fields are loaded on the new stage. None exception is thrown, the server always send the object correctly (tested by printing its vales on console).
My suspicion is that when I click the button to open a new stage, it creates a new instance of the FXML Loader each time. What I don't get is dued to the fact that the execution cycle is always the same both the first time I open the stage and the following ones.
I finally tried to insert a button in the second window which, when clicked, loads the object on the window, and this works correctly also when I open the window multiple times, but obviously I don't like this solution, which would force the user to click the button to retreive data.
Here is the HomePage Controller method that allows to open the new stage:
#FXML
void showSecondView(MouseEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/View_FXML/generaRichiesta.fxml"));
Pane secondPageLayout = loader.load();
SecondController secCon = loader.getController();
stage = new Stage();
stage.setScene(new Scene(secondPageLayout));
stage.setTitle("Second Stage");
stage.show();
}
Here is the Initialize method for the Second Stage
public void initialize(URL url, ResourceBundle resourceBundle) {
//sending request to server and calling the following function to update the
//view withe the received object
Platform.runLater(
() -> {
//setting the view node "textField" with the object received from server
//if I print this value on console I always get the object correctly but
//it is loaded only the first time. When i call this function from a button it always
//works but I should do it manually
textField.setText().receivedObject.toString();
}
);
}
I really don't get what the problem could be, I think it may be because when I create every time and FXMLLoader object.
Any suggestions?
Thanks
I found the error, basically I'm using the Observable Pattern, every time I opened a controller, I associated the controller object in a hashmap and didn't accept other insertion of objects of the same class. Also each time I closed the second stage, I omitted to remove to remove the first instance of the controllere from the map, so the Observable Pattern sent notifications of changes only and always to the first instance of the controller created and not to the subsequent ones that were created by reopening the window

fxmlLoader.load() will not load object hiearchy

I have 2 windows (login and main) with appropriate controllers.
I got this piece of code. This is located in LoginController.java. When I run app, it will open login window from /fxml/login.fxml where the LoginController is set with fx:controller. Then there is Sign In button. Method signIn() is asociated with this button. When i click it, it will close Login window, get controller of main window and open main window from specific fxml file (/fxml/main.fxml). Meanwhile it will get strings from Login window's text fields and pass them to the MainController (those credentials are used in execution of SQL statements).
public void signIn(ActionEvent event) {
MngApi.closeWindow(btn); //closes parent window of specified node
try{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
Parent root1 = fxmlLoader.load();
Stage stage = new Stage();
stage.setTitle("PrintED");
stage.setScene(new Scene(root1, 1058, 763));//1058, 763
MainController ctrl = fxmlLoader.getController();
ctrl.setCredentials(uName.getText(), uPasswd.getText(),
ipAddress.getText(), dbName.getText());
stage.show();
}catch (IOException e){
}
}//signIn end
Now this is the problem: When i click Sign In button, the signIn() method will execute, Login window will close and SUCCESSFUL BUILD will pop up. No other window will open. After debugging I found out that program will stop at this line (everything before is done)
Parent root1 = fxmlLoader.load();
No error, no information, only successfull build. The most mysterious thing for me is that it worked before! Login wondow got closed and main windows appeared. I only added some new methods, action events and stuff. I made no changes to LoginController and fxml file. I only made changes to MainController (added methods and action event for buttons in main.fxml).
I have no idea what's bad and i am clueless. Please help!
Okay so I found out whats wrong. The error was in initilize() method. There i put a method which should initialize columns in my TreeTableView. I thought that when i run program, initialize wil execute (after Controller constructor) and it will run code line by line. so i put there initializeColumns() method, si it will execute first and only once. Looked like this:
public void initialize(){
initializeOrdersColumns();
//this listener will load (refresh) orders automaticaly when Orders Tab is selected
tabOrders.setOnSelectionChanged((event) -> {
if (tabOrders.isSelected()) {
List<TreeItem<Order>> allOrdersList = new ArrayList<>();
stackItems(allOrdersList, root);
root = new TreeItem<>();
ttwOrders.setRoot(root);
ttwOrders.setShowRoot(false);
}
});
//When we click refresh button, orders tab will reload Orders
btnOrdersRefresh.setOnAction((event) -> {
List<TreeItem<Order>> allOrdersList = new ArrayList<>();
stackItems(allOrdersList, root);
root = new TreeItem<>();
ttwOrders.setRoot(root);
ttwOrders.setShowRoot(false);
});
}//end of initialize()
After I deleted content of initialize() method, everything went well, so it was obvious that error code is in there. Then I commented all code except first method (initializeColumns()) and program went wrong. When I commented only initializeColumns() method the program went well again. So it was clear, initializeColumns() cannot be in initialize() method. Don't know why yet, but i will find out.

Wrong record displayed when create form closes

Inside AX2012 R3, when creating a new Return Order from the Return Order list view page (using the button in the Header), the SalesCreateOrder form opens and functions as expected.
Upon close of this form, however, instead of opening the newly created Order, instead the order that was selected in the grid is opening.
Several developers have made customization to this form, but none that (at first glance) appear relevant to this behavior.
Where would I find the behavior to open a form upon close of the SalesCreateOrder dialog?
You can open the created order by changing the SalesCreateOrder.close method:
public void close()
{
Args args = new Args(this); //Change here
// Save user's customer search type
MCRCustSearch::saveCustSearchType(mcrCustSearchType.selection());
if (salesTableType)
{
salesTableType.formMethodClose();
}
//Change here -->
args.record(salesTable);
new MenuFunction(menuitemDisplayStr(SalesTable),MenuItemType::Display).run(args);
//End of change <--
super();
}
You may have to change the called menuitem if called from Return order.
Your understanding of how returns are created is wrong. A form isn't opened upon closing, it's opened upon creation.
When you do Ctrl+n or click to create a new return order, the ReturnTable form actually instantiates the SalesCreateOrder form eventually.
To see this, place a breakpoint in the init method of the ReturnTable at \Forms\ReturnTable\Methods\init and then try and create a new return order.

Show progress while loading FXML file

JavaFX application has a tree menu and clicking on a menu item loads an fxml file image attached
Some of the FXMLs are nested and are taking up to 2-3 seconds to load. I would like to show a progress indicator while FXMLs are getting loaded and initialized. Controller's initialization code can be executed as a Task and bind it's progress to a progress bar control, but the problem is that some of code needs to be executed in FX Application thread using Platform.runLater(). If I do that, there is no way to know when the initialization is completed.
Is there a way to handle this seemingly simple task ?
I cannot share the controller code which is taking long time to load, but for the purpose of this question, please assume the following initialization code is the culprit. I cannot execute this block of code in a Task thread as UI changes cannot be made from threads other than JavaFX Application thread. If I wrap the part of code that changes UI inside Platform.runLater(), task may finish it's execution even before the UI changes are completed.
public void initialize(URL url, ResourceBundle rb) {
if (webEngine == null) {
webEngine = helpView.getEngine();
}
try {
Path filepath = Paths.get("docs/index.html");
webEngine.load(filepath.toUri().toString());
} catch (Exception ex) {
Alert alert = new Alert(Alert.AlertType.WARNING, "Failed to read doc files", ButtonType.OK);
alert.show();
}
}
Raj

Storyboard segue call sequence

I have a button which calls a method on the same view. That button generates a data and takes me to another MapViewControlelr .I have 'prepareForSegue' method on the first view. But that prepareForSegue is called first right after button click. As a result my variables aren't getting updated from the method call. Any idea whats wrong?
I think you should use GCD to load your data asynchronously and then perform the segue, something as follows:
dispatch_queue_t queue = dispatch_queue_create("queue", NULL);
dispatch_release(queue);
dispatch_async(queue, ^(void) {
// Generate data
// Main queue
dispatch_async(dispatch_get_main_queue(), ^(void) {
// perform segue
[self performSegueWithIdentifier: #"MySegue" sender: self];
});
});
Also, instead of configuring the segue from button to newViewController - configure it from mainViewController to newViewController and then use performSegueWithIdentifier method

Resources