JavaFx: how to reference main Controller class instance from CustomComponentController class? - reference

WHAT I HAVE is a standard JavaFX application: Main.java, MainController.java & main.fxml. To add custom component, I created CustomComponentController.java and custom_component_controller.fxml.
PROBLEM is that in CustomComponentController methods I need to reference other methods and standard components from MenuController. I add public static MainController mc; to MainController class body, so that it can be seen from CustomComponentController (MainController.mc.neededMethod()). Then I try to pass everything to it in MainController.initialize() method (mc = this;) - when debugging this breakpoint, I see this full of components instances, but mc remains with null components afterwards.
QUESTION is how to reference the running instance of MainController to use its components and methods in other classes and to crossreference different custom components from each other? How to clean MainController code from event handlers and assistance methods of components by moving it all to component's own class?
I tried the following approaches, but found no way to make them work without errors:
Accessing FXML controller class
How can I access a Controller class in JavaFx 2.0?
JavaFX 2.0 + FXML. Updating scene values from a different Task
JavaFX 2.2 -fx:include - how to access parent controller from child controller

The problem can be solved if you comply the following conditions:
Not only public, but obligatory static MainController mc should be.
Do not forget id in fxml for CustomComponentController: <CustomComponentController fx:id="cc"/>, where cc is the name of the "#FXML imported" CustomComponentController in your MainController class.
Omit parameter fx:controller="main.CustomComponentController" in custom_component_controller.fxml as it results in "Controller value already specified" error (a conflict between main.fxml and custom_component_controller.fxml markup declared controllers).
Put mc = this; in the beginning of MainController's initialize() method. Before using mc in CustomComponentController class, check if it's not null. It can be null when all components, including CustomComponentController, are instantiated at application startup, but there is no mc instance yet. MainController method initialize() where MainController is instantiated is called after components are loaded. Therefore better practice is to use approach in the next paragraph.
In main.fxml create primary component of the same type that CustomComponentController and with the only fx:id parameter. Replace primary component with your CustomComponentController by creating reloadCustomComponents() method and calling it from CustomComponentController's initialize() method. Do it by adding the following to reloadCustomComponents() method:
customComponentAnchorPane.getChildren().remove(customComponent);
customComponent = new customComponent();
customComponentAnchorPane.getChildren().add(customComponent);
Thus all components can be placed outside CustomComponentController with all their methods and reloaded at the startup of the apllication. All component declarations stay in MainController class and can be reached through MainController mc reference. No duplicate creating of components in detail with parameters is needed.

Your problem looks like the classic catalog-crud forms updating, I implemented an interface that I called Updatable with an update method so I could reference any catalog form with any crud form easy after passing Controller Main Class as the UserData Property of the Child Root Component's Form
Hope it Can Solve your problem

Related

Seam page actions and conversation

I have created my application using seam-gen. Seam-gen has created all the crud operations & forms for all my objects. They all inherit from seam's EntityHome.
I have this requirement that I need to create from an object A another object B (A has a List). So I need to redirect the user to the B form, save a new B object, and then redirect him to the original A form with the updated List contents.
I am a newbie in Seam and I am not sure how to implement this properly.
Edit: I am using seam version 2.2.2 final.
You can create an action class (similar to how entityHome works without the baggage that comes with it) to manage your contained entities and their behaviors. If no relationship exists between the entities you can make one here.
Refreshing the original list can be tricky, but once you have some code started post it.
So I would start with something like:
Class ActionBean {
ClassAObj classA;
List<ClassBObj> classBList;
public void methodThatLinksAandB() {
// ... stuff happens here
}
// getters and setter for view
// private worker methods
}

JavaFX access controller's variables from Scene Builder

If I declare something like
#FXML
private final static double PREF_SPACING = 10d;
or
#FXML
private Insets insets = new Insets(10d);
in the controller class,
is there a way to use their values in Scene Builder?
When I want to change the value, I want to change
it only once, in the controller class.
PRELIMINARY ANSWER
I haven't yet tried all of the techniques below, but it seems to be the way you would do it from reading the documentation. If I get some time, I'll try it out later and update this answer with results (or somebody else can do this and post a new answer or edit this one to create a definitive answer). I just wanted to publish something now to point you in what I believe to be the right direction.
If the below is not what you are looking for, add a few more specifics to your questions to fully describe what you want.
Don't using the #FXML annotation here. #FXML is for injecting values from the markup into the controller, not the other way around.
For your first example which is a constant, let's say your controller class is:
class MyControllerType {
public final static double PREF_SPACING = 10d;
}
Then in your fxml, reference the constant:
<?import MyControllerType?>
...
<VBox>
<spacing><MyControllerType fx:constant="PREF_SPACING"/></spacing>
</VBox>
For your second sample which is not a constant or a part of the SceneGraph, you can use an fx:define element to instantiate an instance of the class. Note that you can't directly instantiate an Insets class from FXML as it has no builder class nor zero length constructor. So what you might be able to do is create another placeholder class for the information and instantiate a reference to that in your FXML (or you can create a Builder that FXML can use to instantiate the Insets).
class InsetsHolder {
private Insets insets = new Insets(10d);
public Insets getInsets();
}
<?import InsetsHolder?>
<fx:define>
<InsetsHolder fx:id="i"/>
</fx:define>
<VBox>
<Button text="Click Me!" VBox.margin="$i.insets"/>
</VBox>
SceneBuilder should be able to read fxml files which use the fx:define and fx:constant notation, as well as (possibly) make use of the reference expression $i.insets. SceneBuilder might not have any UI to allow you to edit the values from within the SceneBuilder application, so you will probably need to hand edit the fxml file portions related to the fx:define and fx:constant elements if you wish to make use of these structures.
There is an executable example of using an fx:define element in this mailing list post on designing resolution independent units in FXML.
In general, I think I'd be a bit cautious of maintaining these kind of dependencies between fxml and java code. It may be more prudent to do more of this kind of stuff in plain Java code within the context of the controller initialize method as scottb suggests.
The #FXML annotation enables the JavaFX objects whose names you defined (fx:id) to have their references reflectively injected into nonpublic fields in the controller object as the scene graph is loaded from the fxml markup.
To the best of my knowledge, this is a one way operation. There is no provision for having named static class variables in the controller object injected into the scene graph during loading.
You can accomplish something very similar to what you are requesting by defining the values that you want set as class variables in your controller object's class, and then setting the appropriate object properties programmatically (rather than in markup) in the initialize() method of your controller object.
The initialize() method is called (if it is present) after the loading of the scene graph is complete (so all the GUI objects will have been instantiated) but before control has returned to your application's invoking code.

Loading Data-Backed ComboBox with Simple Types

I have a RPC method that returns a List of Strings. I want to create a ComboBox with a store that will load the values through a RpcProxy, but I can't find an example that doesn't use some sort of ModelData class.
I would prefer not to have to create a simple Bean with only one property (the string) and then have to convert the List one item at a time.
My ideal would be to create something like this:
RpcProxy<List<String>> proxy = new RpcProxy<List<String>>()...
Any suggestions?
Unfortunately, with GXT 2.2.5, you can't get around not using ModelData.
The class definition for ComboBox says it all:
public class ComboBox<D extends ModelData> extends TriggerField<D> implements SelectionProvider<D> {
...
protected ListStore<D> store;
...
So, at this point your biggest concern is keeping your code clean. If you have to make a specialized ModelData derived class, you could subclass ComboBox and keep a nested class definition for your wrapper object.
If you're not tied to using GXT 2.2.5, I would update to GXT 3.0.x and GWT 2.5.0. GXT 3 moved away from using ModelData. Now, everything accepts bean-like objects.

How to access a method of another class(.cs)inside another ascx page(design page)

I have 1 method which returns the count of records,i want to access this method in design page(ascx) .how to do that?
If your method is in the code-behind class for the ascx then method needs to have public or protected (because ascx class will inherit from code behind class) - then you can use server directives or data binding to access the method - for example
<span><%= this.CallMyMethod() %></span>
or
<span><%# this.CallMyMethod() #></span>
For later (data-binding) syntax to work, you must call DataBind method on the parent(ancestor) control.
In case, your method is in another class and its instance method then you need to have instance of that class to call the method. Calling mechanism remains same as above except replace this keyword with the variable (instance) of another class. For static methods, you can invoke them using className.MethodName syntax. Note that the method has to be accessible from ascx (i.e. public or internal etc).

JSF 2.0: When is encodeAll called and when is encodeBegin called?

Consider a custom UIComponent (for test purposes only):
public class UITest extends UIComponentBase {
#Override
public void encodeBegin(FacesContext context) throws IOException {
System.out.println("encodeBegin");
}
#Override
public void encodeAll(FacesContext context) throws IOException {
System.out.println("encodeAll");
}
}
When I add it to page inside a composite component, the encodeBegin() method gets called. However, when add it to page outside a composite component, the encodeAll() method gets called instead.
Adding it inside other UIComponents makes no difference, only composite component wrapper seems to change the behavior.
Couldn't find info why it is so? A link to the spec?
The spec is really messy in this case, stating that:
"These methods are called during the Render Response phase of the request processing lifecycle. encodeAll() will
cause this component and all its children and facets that return true from isRendered() to be rendered, regardless
of the value of the getRendersChildren() return value. encodeBegin(), encodeChildren(), and
encodeEnd()have the responsibility of creating the response data for the beginning of this component, this
component’s children (only called if the rendersChildren property of this component is true), and the ending of
this component, respectively."
However, this seems to be a mixture of new and old features, where the new functionality (encodeAll) seems to be incomplete in some ways:
I tried the following:
A) Calling the component directly in the page (no wrapper)
extend UIComponentBase (or other UIComponent class such as UIInput, UIOutput.. etc), declare it as a tag, and use it in the UI.
In this case the encodeAll method is called IF it is present (overridden), if not the encodeBegin and encodeEnd methods will be called!!
Another thing to note is that you can create a custom Renderer for the component, so you can separate rendering logic from behaviour. (by creating another class that extends Renderer, and annotating it with #FacesRenderer)
This is where it gets interesting; Renderer defines only encodeBegin, encodeChildren and encodeEnd (with no mention of encodeAll).
Now the logic seems to go roughly like this:
if (encodeAll is present)
encodeAll is called (and the renderer is ignored!)
else if(any of encodeBegin,Children,or end exist in the class that extends UIComponent)
call the method that was found in that component
else if(encodeBegin, children or end exist in the class that extends Renderer)
call the corresponding method that was found.
So this means that implementing encodeAll (or encodeBegin.. etc ) in the class extending UIComponent causes the renderer to be ignored!
B) Wrapping the component (cc:implementation.. etc)
In this case the same thing happened as above, except that encodeAll was not called in any case, no matter what I did.
Conclusion:
It seems that encodeAll is some kind of new functionality (or shortcut) to implement the rendering code, and it seems that cc:implementation has a bug in this case (it doesn't look for encodeAll).
I hope this is at least of some value to you, unfortunately I cannot provide a more thorough answer. :(
It also seems that nobody else knows about this.

Resources