I've searched several times here for answer but didn't get my solution.
In my case:
I want to take input from user and check validity. If everything is fine I will grab users ID from database and send that ID to another FXML and then run a select query there using that ID and display the results into a tableView.
In 2nd FXML (controller) I am using initialize() method to set data into tableView and a setId() method to receive user ID from previous FXML. But, initialize() method get called before setId() method and doesn't provide my required result as the ID is null.
Used Passing Parameters JavaFX FXML this method form passing data between FXML.
What will be the best solution for this?
FYI: Currently I'm using an extra class with static variable to store ID.
You could use a controller factory that initializes the id before it returns the controller instance:
FXMLLoader loader = new FXMLLoader(url);
loader.setControllerFactory(c -> {
MyController controller = new MyController();
controller.setId(userId);
return controller;
});
...
Node n = loader.load();
This way you could also use classes as controllers, that don't provide a default constructor. A more complex controller factory could be used to connect model and presenter (see MVP).
An alternative would be to modify the scene's contents in the setId method instead of the initialize method, which would be simpler than using a controller factory.
What the best solution is depends on your needs and personal preference. However, using a static member to pass data should be avoided, if possible.
Related
I'm a newcomer when it comes to JavaFX and I recently encountered a problem which really confuses me alot. I'm using a class called "MainController" which controlls an FXML-File containing a TabPane. Each tab is controlled by another controller. But there is one situation in which a tab needs to be deleted, so I need access to the MainController instance to remove the currently active tab from the pane.
Whenever I'm using this code to get an instance of the currently running MainController, I instead get a completely new instance with all of its components set to their default values.
The code is:
FXMLLoader loader = new FXMLLoader(getClass().getResource("Main.fxml"));
loader.load();
MainController controller = loader.getController();
controller.closeCurrentTab();
protected void closeCurrentTab() {
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedIndex());
}
I'm currently using a static reference to the controller to access it since it is the only solution that works for me. But I know that this is highly unprofessional and I really want to avoid that.
I hope somebody knows what's wrong here.
You should ensure that you have a reference for your main controller at the point where you want to use it. I guess it is one of the "child" controllers (most probably the controller of the current tab).
Therefore if you would have a property in this class that stores the reference for your main controller, your problem would be solved.
I guess you initialize this "child" controller from the main controller like:
FXMLLoader loader = new FXMLLoader(getClass().getResource("TabController1.fxml"));
loader.load();
So here you could do:
TabController controller = loader.getController();
controller.mainControllerProperty.set(this);
Where mainControllerProperty is defined in TabController like:
ObjectProperty<MainController> mainControllerProperty = new SimpleObjectProperty();
I'm currently teaching myself JavaFX, and I've taken a simple example program that's hardcoded the view and am turning it into one that uses FXML (mostly so I can use SceneBuilder for building UIs). Rather than writing a separate controller class, I'm using the application class (so there's 1 Java file and 1 FXML file). I'm not using an initialize() method as it's a linear flow (display the UI, populate the fields, wait for input). The view pops up, but then the app errors out as none of the controls are mapped to the appropriate variables (so for #FXML TableView<...> table, table is null).
However, I put in an initialize() method for debugging, the controls are injected while in initialize(), and then return to null when initialize() exits.
So the question is, does JavaFX instantiate a new instance of the application class as a separate controller class? This would explain why the variable are going out of scope. Or is it something else (e.g. the controls are injected only when being called back from JavaFX actions)?
The default behavior of the FXMLLoader is to create a new instance of the controller class and use that instance as the controller.
Specifically, the FXMLLoader does something like:
Read the root FXML element.
If the root FXML element has a fx:controller attribute, then
If a controller already exists, throw an exception, otherwise create an instance of the specified class1 and set that as the controller
Continue parsing the FXML file. If elements have a fx:id attribute, and a controller exists (by any mechanism), inject those fields into the controller. Similarly register event handlers as calls to methods in the controller instance.
Invoke initialize() on the controller, if a controller exists and it has such a method.
So, the question you asked:
Can application class be the controller class
Yes, but it's probably a terrible idea. If you simply specify the Application subclass as the controller class using fx:controller, then a second instance of the Application subclass is created, #FXML-annotated fields are injected on that second instance, and the initialize() method is invoked on that second instance. Obviously, the #FXML-fields are never initialized on the instance on which start(...) is invoked, and the initialize() method is never invoked on that instance.
The question you probably meant is:
Can the application class instance created at launch be used as the controller?
The answer to this is also yes, and, aside from very small demo programs you intend to immediately discard, it's also probably a very bad idea. You would do this by
public class MyApp extends Application {
#FXML
private Node someNode ;
public void initialize() {
// do something with someNode
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file.fxml"));
loader.setController(this);
Parent root = loader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Note that to use this code, your FXML file must not have a fx:controller attribute.
The problem with this is that you have no separation and no flexibility. (E.g. if you create a second instance of the view defined in your FXML file somewhere, you end up with a second Application subclass instance, which is at best counterintuitive (one application with two Application instances...).)
So I would advocate using a separate class for the controller in basically every case. The Application subclass should contain minimal code and should be used only for starting the application.
1 This step is actually a little more complex. If a class is specified in the fx:controller attribute, and no controller already exists, the FXMLLoader checks for a controllerFactory. If one exists, then the controller is set as the result of passing the specified Class to the controllerFactory's call() method, otherwise it is created by calling newInstance() on the specified class (effectively calling its no-argument constructor).
If you have defined your application class to be the controller in the FXML file, JavaFX will, if I remember correctly, create a new instance of your application class and use the new instance as a controller. Thus, your existing application class still has null for the table.
You can however define the controller programmatically in your application class to use your own instance:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();
I have a Java Bean with a Method that returns an Object of type PaymentItem
Payments.getItem(viewScope.vsRIndex);
this method returns the nth item from an ArrayList<PaymentItem>
I have a button on my main page that renders a Custom Control on that on the main page and sets the viewScope to the correct index value.
<xp:panel id="panelPaymentEntry"
rendered="#{javascript:(viewScope.vsShowPayment) ? true : false;}">
<xc:ccCOMPaymentInput></xc:ccCOMPaymentInput>
</xp:panel><!-- panelPaymentEntry -->
I want to set the dataSource for ccCOMPaymentInput to the PaymentItem returned by Payments.getItem(viewScope.vsRIndex)
I added this code to the createObject
try{
Payments.getItem(viewScope.vsRIndex);
}catch(e){
//do nothing
}
with the var = pItem
But does not appear that the Object pItem has been created.
Am I on the right track? or ?????
Generally, the cleanest way to do this is to create a custom property on the custom control to specify the context object - value is the conventional pick. So you'd have something like:
<xc:ccCOMPaymentInput value="#{javascript:Payments.getItem(viewScope.vsRIndex)}"/>
Then, within the control, you can reference it as compositeData.value. For example:
<xp:inputText value="#{compositeData.value.someTextField}"/>
The Object data source you're presumably referring to can also work, but isn't always necessary.
I'm following the example from the GXT website here: http://www.sencha.com/examples/#ExamplePlace:paginggrid
Their code creates an RPCProxy, overrides load() to make an RPC call to get data and then I assume the listStore is populated in the callback that isn't provided in the example.
Question:
I want to populate the grid with search results so I want the fetching and loading of data to be done in response to sone button select event. I don't want to load the grid with data when it's created. I can't figure out how to refactor this example to do that.
I want to populate the grid with search results so I want the fetching and loading of data to be done in response
Just make sure you override the load method of RpcProxy class correctly, it will make an RPC call to your servlet and pass the search criteria, then receive the appropriate data.
I don't want to load the grid with data when it's created.
The RpcProxy object was passed to loader constructor, which mean the one controlling the RpcProxy object was the loader object. The grid by default was never loaded with data when it was created (unless we add the code to do that). The data was loaded everytime the method load of loader object was called, not when the object of loader or RpcProxy or even Grid object was created.
Finally, here is some example code to search data using RpcProxy :
RpcProxy<PagingLoadConfig, PagingLoadResult<Post>> proxy = new RpcProxy<PagingLoadConfig, PagingLoadResult<Post>>() {
#Override
public void load(PagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<Post>> callback) {
service.getPostsBySearchCriteria(loadConfig, searchCriteria, callback); // make sure your rpc service receive search criteria parameter
}
};
Hope this could help you :-)
In my App (Fusion Web) exist a ViewObject from Oracle DB.
I created the java classes and build a specific method (makeUniqueSearchByDate(String)) to process the data.
This method appears in "Data controls" that I can drag to the "view" and use as any other function. When I try to use it in a "bean" (instead of dragging directly):
public void setDate(ActionEvent actionEvent) {
ApplicationModule appMod =
Configuration.createRootApplicationModule("com.svr.model.AppModule", "AppModuleLocal");
ViewModelosByDataImpl fo = (ViewModelosByDataImpl) appMod.findViewObject("ViewModelosByData1");
String dateV = "07-01-2013";
fo.makeUniqueSearchByDate(dateV);
}
This code has no effect on the table. Can anyone see why?
Btw, the program does not throw any exception. Just does not work. The table remains the same. But if I use the button, automatically generated by "drag and drop" the function runs normally. I know I should study ADF, but unfortunately I have no time.
i think after you have exposed the method written at VO as Client interface, you need to create a method binding in pageDef file of you page. after creating the method binding, you need to access the method in bean through binding layer something like this :
OperationBinding op=((DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry()).getOperationBinding("Method Binding");
op.execute();
i think the method used by you to call VO method from bean is not right.
i think one more thing you need to do in your bean after calling the VO method is that you need to do refresh the table / perform PPR programatically :
AdfFacesContext adfFacesContext = AdfFacesContext.getCurrentInstance();
adfFacesContext.addPartialTarget(component binding for your table component);
you can try setting autosubmit to true for command button which invokes action event, and set partial trigger for table to component id of the command button.
can you post VO method code as well ?
does the method get called and data gets committed / updated when you execute it through bean ? is it only a table refresh issue ? do you see changes to data if you manually refresh the page ?