I want to achieve generic code for loading fxml and set data for that fxml. Following is my function to load child fxml. Now i want to figure out how i can get controller name from getLoader and do not need to hard-code OwnerViewController. So i can set data for each fxml pass to the function argument. My controller have OwnerViewController setData method and its working fine. I just want to make my controller name to dyanamic.
public void loadAnchorPaneWithData(AnchorPane ap, String a, Object data) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/herudi/view/"+a));
AnchorPane p = loader.load();
OwnerViewController controller = loader.getController();
controller.setData(data);
ap.getChildren().setAll(p);
} catch (IOException e) {
}
}
Related
I have read a few of the related posts on this topic but have been unable to use them to solve my issue. I believe my failure is one of comprehension and not that I am facing a unique issue. But, I am at a total impasse.
I'm building a CRUD application using JavaFX. One of my application's buttons, "Import Data," is throwing NullPointerExceptions:
// *a button that opens a new window with a textField where the user can paste text data*
#FXML
private void importDataButton(ActionEvent event) {
// *load the fxml file*
URL viewLocation = getClass().getResource("/importView.fxml");
// *get the file's controller*
FXMLLoader loader = new FXMLLoader();
ImportController importController = loader.getController();
importController.setMainController(this);
loader.setLocation(viewLocation);
try {
loader.load();
} catch (IOException exception) {
System.out.println("IO Exception thrown.");
return;
}
....
}
I'm not very good with IntelliJ's debugger yet, but I've used it to determine that the FXMLLoader object is null. So when
importController.setMainController(this);
executes, a NullPointerException is thrown because the object this refers to is null. I...think. So then
ImportController importController = loader.getController();
cannot retrieve the controller from the FXMLLoader object (loader).
For reference, setMainController() is in another class called ImportController, and that method's code is as follows:
public void setMainController(MainController mainController) {
this.mainController = mainController;
}
Things I've tried:
I read this post and this post, both of them recommend that I must run loader.load() [given FXMLLoader loader = new FXMLLoader()] in order to retrieve data from the object. However, I've tried this, and I just get errors upon errors: InvocationTargetExceptions and IllegalStateExceptions. I have also tried instantiating an FXMLLoader object that is non-null, using
FXMLLoader load = new FXMLLoader(getClass.getResource("sample.fxml"));
But it seems to have no effect on the content of the object (and yes, I am replacing "sample.fxml" with my filename.)
I hate to make a similar post, but I have no idea what to do.
The problem is that you are calling loader.getController() before loader.load(). That's why your importController is null when calling importController.setMainController(this).
Call loader.load() first:
URL viewLocation = getClass().getResource("/importView.fxml");
FXMLLoader loader = new FXMLLoader(viewLocation);
try {
loader.load();
ImportController importController = loader.getController();
importController.setMainController(this);
} catch (IOException exception) {
exception.printStackTrace();
}
But be aware that the initialize() method in your ImportController is called before setMainController().
I have an app, which has HomeScene.fxml file with headers and menu. HomeScene has also dashboardPane, which should be changed dynamically after menu button is being pressed. Dashboard pane content should be loaded from another fxml file, lets say 'FinancesPane.fxml' or 'SettingsPane.fxml'.
Im trying to replace content of dashboardPane in HomeController:
#FXML
public void handleFinancesButtonAction() {
FinancesPaneFactory paneFactory = new FinancesPaneFactory();
dashBoardPane.getChildren().clear();
dashBoardPane.getChildren().add(paneFactory.createPane());
}
My FinancesPaneFactory looks like this:
public class FinancesPaneFactory extends PaneFactory {
private static final String PANE_TEMPLATE_PATH = "/sceneTemplates/FinancesPane.fxml";
public FinancesPaneFactory() {
super(PANE_TEMPLATE_PATH );
}
#Override
protected Pane generatePane(FXMLLoader loader) {
try {
return (Pane) loader.load();
} catch (IOException e) {
throw new FatBirdRuntimeException("Unable to load FinancesPane", e);
}
}
}
To be more clear, this is how HomeScene looks like: HomeScene .
This empty space is a dashboardPane, and should be replaced with another content when user press the left menu button.
How to inject this content dynamically?
Yes, you should do this to keep scene graph low and you will benefit from better performance , what i do is create dynamic container :
#FXML
private ScrollPane dynamicNode;
Scroll pane is a good choice.
This is put to MainController.
I have main controller different from others , main controller is actually the only one i initialize, so in your main program class whatever you call it :
private static MainViewController mainViewController;
...
private static BorderPane loadMainPane() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setController(mainViewController);
BorderPane mainPane = (BorderPane) loader.load(
CsgoRr.class
.getResourceAsStream(Info.Resource.FXML_FILE_MAIN));
mainPane.getStylesheets().add(CsgoRr.class.getResource("path...style.css").toString());
return mainPane;
}
Dont forget to create static accessor, other controllers that i have are usually not created this way , i use fx:controller in fxml to specify what controller should be for which fxml , its usually handy to have mainController accessable.
So to change your views create in your main controller methods that are connected to your menu with whose you change views
#FXML
private void setViewPreferences() {
setView(Info.Resource.FXML_FILE_PREFERENCES);
}
#FXML
private void setViewProductPage() {
setView(Info.Resource.FXML_FILE_PRODUCT_PAGE);
}
Currently in dynamicNode is helper to see what exactly is the current selected, its
private String currentlyInDynamicPane;//not important
Here is setView
public void setView(String fxmlPath) {
dynamicNode.setContent(getView(fxmlPath));
currentlyInDynamicPane = fxmlPath;
}
public Node getView(String fxmlPath) {
try {
return new FXMLLoader(getClass().getResource(fxmlPath)).load();
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
So when you click left menu you swap FXML files, you can make sure that you have some default FXML shown at the start or when nothing in menu is selected as well.
This is the way i do it, roughly.
So think about YOUR DASHBOARD as DynamicPane,
Link to tutorial explaining the code.
https://www.youtube.com/watch?v=5GsdaZWDcdY
Overview:
class ScreensController extends StackPane //keep pane so we can remove add screens on top/bottom
It has this method for loading multiple screens.
public boolean loadScreen(String name, String resource) {
try { //fxml file
FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
//System.out.println(resource);
Parent loadScreen = (Parent) myLoader.load();//class cast to controlled screen
ControlledScreen myScreenControler = ((ControlledScreen) myLoader.getController());//Returns the controller associated with the root object.
//inject screen controllers to myscreencontroller
myScreenControler.setScreenParent(this);// inject screen controllers to each screen here
addScreen(name, loadScreen);
return true;
} catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
}
Each screen controller has this object
ScreensController myController;
And this method for switching between screens (switch which controller is in control)
#FXML
private void goToScreen2(ActionEvent event){
myController.setScreen(ScreensFramework.screen2ID);
}
public interface ControlledScreen {
//This method will allow the injection of the Parent ScreenPane
public void setScreenParent(ScreensController screenPage);
}
This is a very brief synopsis and I'm sure I'm leaving out integral parts of the code.
My question is basically How can I send something (anything) from one screen to another?
I'd also like to be able to have a universal model object that all the controllers can access and control.
If you take a look at my code here
You will see I am putting all my model objects into the "initialize" method of each SEPARATE controller.
If any of you can give me some pointers or feedback on my code I would really appreciate it. Thanks.
im trying to add some items to my View Table . im creating a ObservableList and pass it to the setItem method but it throws null Exception .
Here is the latest attempt :
javafx.scene.control.TableView table = controller.friendsList;
/*
the controller.friendList is defined as Below :
#FXML
public javafx.scene.control.TableView friendsList;
*/
ObservableList friendData = FXCollections.observableArrayList();
for(Client friend : friends )
{
friendData.add(friend.getUsername());
}
//The friendData is fine ( according to the debugging ) it includes 2 Strings
table.setItems(friendData);
table.setEditable(false);
table.setVisible(false);
table.setVisible(true);
}
whats wrong with the code ?
EDIT:
now i get the controller from the FXMLLoader as the answere says . but it's still the same . all the field with the #FXML anotation are still null .
here is how i get the controller :
public userSceneController controller;
FXMLLoader fxmlLoader = new FXMLLoader();
Parent root = fxmlLoader.load(getClass().getResource("userScene.fxml"));
controller = (userSceneController) fxmlLoader.getController();
Since your TableView is a #FXML-annotated field in the controller, it is initialized in the controller instance created by the FXMLLoader. In other words, presumably at some point in the code you didn't actually show, you create an FXMLLoader and call load() on it. As part of that load process, the FXMLLoader creates an instance of the controller and initializes the #FXML-annotated fields with references to the controls displayed in the UI.
You are presumably intending to configure the table view that is displayed in the UI; in order to do that, you need to reference the controller that the FXMLLoader created for you.
However, what you actually do in your code is to create a new controller instance:
Controller controller = new Controller();
Obviously since this in not the controller created as part of the FXMLLoader.load() process, it never had its friendsList field initialized.
Instead, you need to retrieve the controller instance from your FXML loader. In order to do this, you must create an FXMLLoader with a location properly set, and then use the instance method FXMLLoader.load(), not the static method FXMLLoader.load(URL):
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("userScene.fxml"));
Parent root = (Parent) fxmlLoader.load();
controller = (userSceneController) fxmlLoader.getController();
I have a Controller, let's call it GeneralController, that creates a TabPane as well as two new tabs. The new tabs all get their own controller, "Tab1Controller" and "Tab2Controller".
Within the GeneralController, I create an Object "MyObject". This Object contains some data, that can be modified within Tab1Controller and Tab2Controller.
So far, so good.
"Tab1Controller" and "Tab2Controller" both have a initController function, which gets "MyObject" as a parameter. This way, I can initalize both Controllers with "MyObject".
GeneralController:
// Similar function for Tab2Controller
private void createTab1(ObjectProperty<MyObject> myObject) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("path/to/tab1.fxml"));
Tab tab1 = (Tab) loader.load();
Tab1Controller tab1Controller = loader.getController();
tab1Controller.initController(myObject)
generalTabs.getTabs().add(tab1);
}
catch (IOException ex) {
ex.printStackTrace();
}
}
Tab1Controller/Tab2Controller:
public void initController(ObjectProperty<MyObject> myObject) {
this.myObject = myObject;
}
Here comes my question:
How do I ensure in the best way, that I keep my Labels, Controls, Nodes, Charts, whatever, up-to-date within Tab1Controller and Tab2Controller?
Is it reasonable to create a setOnSelectionChanged-Listener in Tab1Controller and Tab2Controller, and update all possible data-changes?
A good solution would be using the observer pattern.
If you need a good example, take a look at this