I'm calling a method to load a window by passing some parameters to a specific internal method of this window, but I've got this exception:
GRAVE: null
javafx.fxml.LoadException: Controller value already specified.
unknown path:12
here is my method
public void openDef(String sys, String comp, String code) throws Exception {
Stage defStage = new Stage();
FXMLLoader loader = new FXMLLoader();
DefTableController cont = new DefTableController();//calling class controller
loader.setController(cont);
Parent frame = loader.load(getClass().getResource("defTable.fxml").openStream());
cont.getSysChoice(sys, comp, code);//call the method by passing parameters
Scene sceneDef = new Scene(frame);
defStage.setTitle("Défaillance du " + comp);
defStage.setScene(sceneDef);
defStage.show();
}
I don't understand why it consider that the controller is already set? and how to fix that ?
thank you
Remove the fx:controller attribute from the FXML file. That attribute is an instruction to the FXMLLoader to create a new controller: since you have already set one by calling setController it is contradictory.
JavaFX Error: Controller value already specified
This guy answered it ^ Props to him!
Related
I have a series of popups where I edit or view specific objects. I use these popups for editing various objects that are handled by and ORM (ORMLite), what I am trying to achieve is to have a generic/abstract class that implements similar behaviours through encapsulating methods. As I don't want to have the same FXML for all of the
popup dialogs what I came up with was to create a "template" FXML, load it through loadFXML() function provided by Griffon and store it in a Node object to be the root of the created Scene at the Abstract Class. I am familiar with dependency injection, but I am not aware of the AST of the framework so my Abstract class is able to call loadFromFXML() within the Abstract class I created.
I post my code here:
Concrete class implementing the abstract ViewPopUp class I created:
#ArtifactProviderFor(GriffonView.class)
public class VerConductoresView extends AbstractViewPopUp<ObservablePlanilla> {
private VerConductoresController controller;
private ConductoresModel model;
VerConductoresView() {
super(ObservablePlanilla.class, Conductor.class);
nodeM = new GridPane();
super.setController(controller);
}
#Override
public void initUI() {
Stage stage = (Stage) getApplication()
.createApplicationContainer(Collections.<String,Object>emptyMap());
stage.setTitle(getApplication().getConfiguration().getAsString("application.title"));
stage.setScene(init());
stage.sizeToScene();
getApplication().getWindowManager().attach("ver-conductores", stage);
}
}
Abstract view PopUp I created:
public abstract class AbstractViewPopUp<T> extends AbstractJavaFXGriffonView {
protected Class klazz;
protected Class<T> klazz2;
protected Scene viewScene;
protected ViewControllerPopUp viewController;
protected TableView tableView;
protected GridPane gridPane;
protected String[] ignoredNames;
protected String[] columnNames;
protected IModel<T> viewModel;
protected Node nodeM;
#MVCMember
public void setController(ViewControllerPopUp controller) {
this.viewController = controller;
}
AbstractViewPopUp(Class<T> k1, Class k2, Node node){
klazz = k2;
klazz2 = k1;
nodeM = node;
nodeM = loadFromFXML("com.softgan.viewPopUp");
nodeM = node;
}
AbstractViewPopUp(Class<T> k1, Class k2){
klazz = k2;
klazz2 = k1;
nodeM = loadFromFXML("com.softgan.viewPopUp");
}
protected Scene init() {
Scene scene = new Scene(new Group());
if (nodeM instanceof Parent) {
scene.setRoot((Parent) nodeM);
} else {
((Group) scene.getRoot()).getChildren().addAll(nodeM);
}
connectActions(nodeM, viewController);
connectMessageSource(nodeM);
return scene;
}
}
I want to load the FXML through the Abstract class and then store it so the concrete class can access the loaded FXML so I am able to manipulate its contents, adding labels and textfields dynamically. The problem seems to be that loadFromFXML is throwing a NullPointerException as it is not able to resolve the FXML file from the resources. I already tried to use an AST transformation to make it resources aware, but it seems to not be a valid approach as Guice is not able to resolve, I think, the ResourceHandler.
EDIT
This is the Stacktrace I am getting:
[griffon-pool-1-thread-2] WARN org.codehaus.griffon.runtime.core.controller.AbstractActionManager - An exception occurred when executing com.softgan.ConductoresController.view
griffon.exceptions.InstanceMethodInvocationException: An error occurred while invoking instance method com.softgan.ConductoresController.view()
at griffon.util.GriffonClassUtils.invokeExactInstanceMethod(GriffonClassUtils.java:3186)
Caused by: griffon.exceptions.GriffonException: An error occurred while executing a task inside the UI thread
at com.softgan.ConductoresController.view(ConductoresController.java:122)
at griffon.util.MethodUtils.invokeExactMethod(MethodUtils.java:407)
at griffon.util.MethodUtils.invokeExactMethod(MethodUtils.java:356)
at griffon.util.GriffonClassUtils.invokeExactInstanceMethod(GriffonClassUtils.java:3182)
Caused by: java.util.concurrent.ExecutionException: griffon.exceptions.InstanceNotFoundException: Could not find an instance of type com.softgan.VerConductoresView
... 4 more
Caused by: griffon.exceptions.InstanceNotFoundException: Could not find an instance of type com.softgan.VerConductoresView
Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) Error injecting constructor, java.lang.NullPointerException
at com.softgan.VerConductoresView.<init>(VerConductoresView.java:31)
while locating com.softgan.VerConductoresView
1 error
at com.google.inject.internal.InternalProvisionException.toProvisionException(InternalProvisionException.java:226)
at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1053)
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1086)
Caused by: java.lang.NullPointerException
at com.softgan.AbstractViewPopUp.<init>(AbstractViewPopUp.java:72)
at com.softgan.VerConductoresView.<init>(VerConductoresView.java:31)
at com.softgan.VerConductoresView$$FastClassByGuice$$d0c2bde8.newInstance(<generated>)
at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:89)
at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114)
at com.google.inject.internal.ConstructorInjector.access$000(ConstructorInjector.java:32)
at com.google.inject.internal.ConstructorInjector$1.call(ConstructorInjector.java:98)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:112)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:120)
at com.google.inject.internal.ProvisionListenerStackCallback.provision(ProvisionListenerStackCallback.java:66)
at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:93)
at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:306)
at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1050)
... 1 more
[griffon-pool-1-thread-2] ERROR griffon.core.GriffonExceptionHandler - Uncaught Exception. Stacktrace was sanitized. Set System property 'griffon.full.stacktrace' to 'true' for full report.
griffon.exceptions.InstanceMethodInvocationException: An error occurred while invoking instance method com.softgan.ConductoresController.view()
at griffon.util.GriffonClassUtils.invokeExactInstanceMethod(GriffonClassUtils.java:3186)
Caused by: griffon.exceptions.GriffonException: An error occurred while executing a task inside the UI thread
at com.softgan.ConductoresController.view(ConductoresController.java:122)
at griffon.util.MethodUtils.invokeExactMethod(MethodUtils.java:407)
at griffon.util.MethodUtils.invokeExactMethod(MethodUtils.java:356)
at griffon.util.GriffonClassUtils.invokeExactInstanceMethod(GriffonClassUtils.java:3182)
Caused by: java.util.concurrent.ExecutionException: griffon.exceptions.InstanceNotFoundException: Could not find an instance of type com.softgan.VerConductoresView
... 4 more
Caused by: griffon.exceptions.InstanceNotFoundException: Could not find an instance of type com.softgan.VerConductoresView
Caused by: com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) Error injecting constructor, java.lang.NullPointerException
at com.softgan.VerConductoresView.<init>(VerConductoresView.java:31)
while locating com.softgan.VerConductoresView
1 error
at com.google.inject.internal.InternalProvisionException.toProvisionException(InternalProvisionException.java:226)
at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1053)
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1086)
Caused by: java.lang.NullPointerException
at com.softgan.AbstractViewPopUp.<init>(AbstractViewPopUp.java:72)
at com.softgan.VerConductoresView.<init>(VerConductoresView.java:31)
at com.softgan.VerConductoresView$$FastClassByGuice$$d0c2bde8.newInstance(<generated>)
at com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:89)
at com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114)
at com.google.inject.internal.ConstructorInjector.access$000(ConstructorInjector.java:32)
at com.google.inject.internal.ConstructorInjector$1.call(ConstructorInjector.java:98)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:112)
at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:120)
at com.google.inject.internal.ProvisionListenerStackCallback.provision(ProvisionListenerStackCallback.java:66)
at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:93)
at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:306)
at com.google.inject.internal.InjectorImpl$1.get(InjectorImpl.java:1050)
... 1 more
UPDATE
I already found what the problem was. The constructor was not aware of the loadFromFXML method as in the constructor of the view the UI has not been loaded yet. What I did was simply put the loadFromFXML() inside the init() method of the Abstract Class and call it directly from the Concrete View Class. I found out this by calling the loadFromFXML from the initUI method, which is where the UI can be accessed.
AST transformations only apply if you're compiling Groovy code, which may not be what you're doing. The loadFromFXML() method expects a resource to be available on the classpath by matching the given argument using the following value transformation
arg.replaceAll('.', '/') + ".fxml"
This means your code resolves "com.softgan.viewPopUp" to "com/softgan/viewPopUp.fxml". Does that file exist in src/main/resources or griffon-app/resources?
with the ScheneBuilder I defined the controller class of my fxml, the code genereted inside my AnchorPane tag is:
fx:controller="demo.SplashController"
now I would like if I had args in the main, to load a new version of the controller, using the appropriate construct. I try this code in the Application.start:
FXMLLoader loader = new FXMLLoader(getClass().getResource("page.fxml"));
PageController controller;
if(!dir.equals("")){ //attribute coming from args
controller = new PageController(dir);
}else{
controller = new PageController();
}
loader.setController(controller);
AnchorPane root = loader.load();
Scene scene = new Scene(root,480,414);
primaryStage.setScene(scene);
primaryStage.show();
but using this code a conflict appears because I have already defined the controller in my project with FXML code, to solve it would be enough to remove the segment in the FXML code but I would not do it because leaving the code in the fxml allows me to access some good features of the SceneBuilder.
The only way pass parameters to the controller's constructor and specify the controller's class in the fxml is to use a controller factory:
FXMLLoader loader = new FXMLLoader(getClass().getResource("page.fxml"));
loader.setControllerFactory(cl -> dir.isEmpty() ? new PageController() : new PageController(dir));
AnchorPane root = loader.load();
Another option would be to create a method in the controller class that allows you to pass the info after loading and does the initialisation:
FXMLLoader loader = new FXMLLoader(getClass().getResource("page.fxml"));
AnchorPane root = loader.load();
PageController controller = loader.getController();
controller.setDir(dir);
Note that the method call happens after the initialize method is run assuming there is one.
I used some commands for getting the controller of a fxml file. at first I used an address like this:
fx:controller="PersonOverviewController"
and the code in main class was like this
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("view/PersonOverview.fxml"));
AnchorPane ap = loader.load();
PersonOverviewController pc = loader.getController();
pc.setTableContent(this);
but it doesn't work.
in another attempt, I changed the
fx:controller="address.view.PersonOverviewController"
and this time it worked.
Why this is the case?
FXMLLoader needs the binary name of the controller class to be specified. If the package of your PersonOverviewController is address.view you therefore have to include it in the attribute value.
FXMLLoader basically creates the controller instance like this, if no controller factory is set:
String fxController = ...
Class controllerClass = getClassLoader().loadClass(fxController);
Object controller = controllerClass.newInstance();
I am having the following problem with a program that I am currently writing, and I have searched on the internet, but I couldn't really find anything to help me understand the following problem
So inside another class I have written a method that executes this whenever the search button is clicked and the method looks like this:
public void searchButton(){
try {
new SearchController().display();
} catch (IOException e) {
e.printStackTrace();
}
}
And then the SearchController class looks something like this (I simplified it here):
public class SearchController {
#FXML
private Button cancelButton;
#FXML
private Label what;
private static Stage stage;
private static BorderPane borderPane;
#FXML
public void initialize(){
what.setText("Testing"); // this woks
cancelButton.setOnAction(e -> stage.close());
}
public void display() throws IOException {
stage = new Stage();
stage.setResizable(false);
stage.setTitle("Product search");
stage.initModality(Modality.APPLICATION_MODAL);
FXMLLoader loader = new FXMLLoader();
loader.setLocation(SearchController.class.getResource("Search.fxml"));
borderPane = loader.load();
Scene scene = new Scene(borderPane);
stage.setScene(scene);
//what.setText("Testing") and this doesn't work
stage.showAndWait();
}
}
Can someone please tell me why it is possible to write text on the initialize method (that method gets called after the borderPane = loader.load(); line...so why doesn't it work if I try to write on the label after that line?)
Thank you in advance
The FXMLLoader creates an instance of the class specified in the fx:controller attribute of the FXML root element. It then injects the elements defined in the FXML file into the controller instance it created when the fx:id attributes match the field names. Then it calls the initialize() method on that instance.
You create an instance of the controller "by hand" with new SearchController(). This is not the same object that is created by the FXMLLoader. So now when you have loaded the fxml file you have two different instances of SearchController. So if you call what.setText(...) from the display() method, you are not calling it on the controller instance created by the FXMLLoader. Consequently, what has not been initialized in the instance on which you are calling what.setText(...), and you get a null pointer exception.
Since initialize() is invoked by the FXMLLoader on the instance it created, when you call what.setText(...) from the initialize() method, you are calling it on the instance created by the FXMLLoader, and so the FXML-injected fields for that instance have been initialized.
Here is the code that set the user data.
FXMLLoader loader = new FXMLLoader(getClass().getResource(page.toString()));
Parent node = (Parent) loader.load();
loader.getController().setParent(handler);
controllers.put(page.name(), loader.getController());
controller = controllers.get(page.name());
node.setUserData(page.name());
node.getProperties().put("page", page.name());
the variable page is an Enum object, I'm using the name 'page.name()' as the user data
Here is the code that gets the user data:
while (iterator.hasNext()) {
Node node = iterator.next();
String name = node.getProperties().get("page").toString();
System.out.println(name);
....
}
now this what happens:
the System.out.println(name); prints out the user data correctly,
and then I get a NullPointException pointing to
String name = node.getProperties().get("page").toString();
the point is System.out.println(name); that prints the user date
prints it correct.
What's going on here?
Try breaking it down. Code is usually easier to read when you do not chain multiple method calls together.
Node node = iterator.next();
ObservableMap<Object, Object> properties = node.getProperties(); // Swap Object with whatever subclass it is supposed to return
Object nameProperty = properties.get("page"); // Same here
String name = nameProperty.toString();
Then see which line returns the null pointer exception and try searching for the reason behind it. It is likely that either getProperties() or get("page") is in fact returning a null value.