Home » Eclipse Projects » e(fx)clipse » Loading an FXML file and inject its controller into an E4 Part automatically(What's the smartest way to load an FXMl file and inject its controller into an E4 Part)
Loading an FXML file and inject its controller into an E4 Part automatically [message #1384994] |
Tue, 03 June 2014 10:01  |
Eclipse User |
|
|
|
Hi,
in my E4 application, every E4 Part has a corresponding FXMl file + controller:
XYZPart.java
XYZ.fxml
XYZController.java
I would like to load the FXML file and inject the controller instance into the Part whenever the Part instance is being created:
public class XYZPart {
@Inject
public void init(BorderPane pane, XYZController controller) {
pane.setCenter(controller.getRootNode());
// further calls on the controller instance
}
}
This wiring should happen automatically by an external instance, as I don't want the Part to bother with the details of the FXML loading process.
Could this be achieved using an add-on in the application model? I may look something like this:
public class PartRendererAddon {
private EventHandler eventHandler;
@PostConstruct
public void execute(final IEventBroker eventBroker, @ContextValue(contextKey="controller") ContextBoundValue<XYZController> value) {
eventHandler = new EventHandler() {
@Override
public void handleEvent(final Event event) {
// not sure if this is the correct property
Object part = event.getProperty(UIEvents.EventTags.ELEMENT);
if (part instanceof MPart) {
renderPart((MPart) part, value);
}
}
};
// not sure if this is the correct event
eventBroker.subscribe(UIEvents.UIElement.TOPIC_WIDGET, eventHandler);
}
private void renderPart(MPart part, ContextBoundValue<XYZController> value) {
String controllerPath = getControllerPath(part);
Object controller = loadFXML(controllerPath);
// add to the context
value.publish(controller);
}
}
private Object loadFXML(final String path) {
FXMLLoaderFactory fxmlLoaderFactory = getFXMLLoaderFactory();
// load FXML and return controller instance
}
private FXMLLoaderFactory getFXMLLoaderFactory() {
//
}
private String getPath(final MPart part) {
// calculate path to FXML based on part's name
}
}
As I see it, there are at least two open questions in this approach:
1. how do we get the correct FXMLLoaderFactory instance for the part?
2. will the controller instance be destroyed when the part and its context is being destroyed?
And are there any smarter or more elegant solutions than using an add-on?
Thanks,
Uwe
[Updated on: Tue, 03 June 2014 10:02] by Moderator
|
|
|
Re: Loading an FXML file and inject its controller into an E4 Part automatically [message #1385393 is a reply to message #1384994] |
Fri, 06 June 2014 02:43   |
Eclipse User |
|
|
|
It took some time to respond by we already talked about this in our
company a few weeks back. I need to talk to one of my co-workers but one
of my ideas was that the Part could simple be the controller not?
This would give you XYZPart.java & XYZ.fxml we could Convention over
Configuration and teach our PartRenderer the concept of this. Then you
only have to tag the MPart e.g. as FXML and you are ready to go. Another
route would be to introduce a new MPart subclass but I'd prefer to use
Convention over Configuration.
Tom
On 03.06.14 16:01, Uwe San wrote:
> Hi,
>
> in my E4 application, every E4 Part has a corresponding FXMl file +
> controller:
>
> XYZPart.java
> XYZ.fxml
> XYZController.java
>
> I would like to load the FXML file and inject the controller instance
> into the Part whenever the Part instance is being created:
>
>
> public class XYZPart {
>
> @Inject
> public void init(BorderPane pane, XYZController controller) {
> pane.setCenter(controller.getRootNode());
> // further calls on the controller instance
> }
> }
>
> This wiring should happen automatically by an external instance, as I
> don't want the Part to bother with the details of the FXML loading process.
>
> Could this be achieved using an add-on in the application model? I may
> look something like this:
>
>
> public class PartRendererAddon {
>
> private EventHandler eventHandler;
>
> @PostConstruct
> public void execute(final IEventBroker eventBroker,
> @ContextValue(contextKey="controller")
> ContextBoundValue<XYZController> value) {
> eventHandler = new EventHandler() {
> @Override
> public void handleEvent(final Event event) {
> // not sure if this is the correct property
> Object part = event.getProperty(UIEvents.EventTags.ELEMENT);
> if (part instanceof MPart) {
> renderPart((MPart) part, value);
> }
> }
> };
>
> // not sure if this is the correct event
> eventBroker.subscribe(UIEvents.UIElement.TOPIC_WIDGET,
> eventHandler);
> }
>
> private void renderPart(MPart part, ContextBoundValue<XYZController>
> value) {
> String controllerPath = getControllerPath(part);
> Object controller = loadFXML(controllerPath);
> // add to the context
> value.publish(controller);
> }
> }
>
> private Object loadFXML(final String path) {
> FXMLLoaderFactory fxmlLoaderFactory = getFXMLLoaderFactory();
> // load FXML and return controller instance
> }
>
> private FXMLLoaderFactory getFXMLLoaderFactory() {
> //
> }
>
> private String getPath(final MPart part) {
> // calculate path to FXML based on part's name
> }
> }
>
>
> As I see it, there are at least two open questions in this approach:
>
> 1. how do we get the correct FXMLLoaderFactory instance for the part?
> 2. will the controller instance be destroyed when the part and its
> context is being destroyed?
>
> And are there any smarter or more elegant solutions than using an add-on?
>
> Thanks,
> Uwe
|
|
| | | | | | | |
Re: Loading an FXML file and inject its controller into an E4 Part automatically [message #1385429 is a reply to message #1385428] |
Fri, 06 June 2014 07:36   |
Eclipse User |
|
|
|
On 06.06.14 13:15, Jacek Bukowski wrote:
> Regarding the first code example in this thread. Will the part's context
> be already created at the point when UIEvents.UIElement.TOPIC_WIDGET is
> handled? Is the way presented (so using Addon) a good way of
> initializing something (it doesn't necessary be FXMLs) to be available
> in part's @PostConstruct method?
At the moment the TOPIC_WIDGET is sent the UI has been fully constructed
== the Part-Instance has been created (constructor-injection,
field-injection, method-injection,@PostConstruct-Injection) has been done.
The first thing created for an element who inherits from MContext is to
create the IEclipseContext for it TOPIC_CONTEXT so most likely this is
the better point to initialize the the controller!
Generally speaking I'm not a huge fan of doing stuff that way because it
does not keep all relevant stuff in one place. I'd more like to have all
informations in the e4xmi because this would allow more tooling
possibilities e.g. the e4xmi-editor could show a preview of UI loading
the FXML, show error markers if you controller & fxml don't match.
>
> I also think it is a nice use case to have FXML controller separated
> from the part code. I am thinking now about more advanced parts, where
> there is lot's of "application logic code". In that case I would like to
> keep JavaFX-related code in the controller and "application logic" code
> in the part class. We could then inject one into another (e.g. by
> setters). It could still be based on Convention over Configuration. If
> there are XYZ.fxml and XYZPart.java, then part acts as controller. If
> there are XYZ.fxml, XYZController.java and XYZPart.java, then controller
> is separated from part.
>
I think I need to think about this some more but we should discuss this
on the bug Uwe created.
Tom
|
|
| | | | | |
Re: Loading an FXML file and inject its controller into an E4 Part automatically [message #1385748 is a reply to message #1385746] |
Tue, 10 June 2014 17:58   |
Eclipse User |
|
|
|
On 10.06.14 23:48, Boris I. wrote:
> On 10.06.14 14:53, Uwe San wrote:
>> Obtaining the controllers works pretty nicely using the
>> FXMLLoaderFactory, but what about other custom objects that you would
>> like to inject into your presenter? These objects are not referenced in
>> FXML, they are just dependencies of your presenter (e.g. sub-presenters
>> or non-E4 services or)? I think this is what Jacek and "Eclipse User"
>> were referring to when they mentioned a general purpose DI framework.
>> The most important aspect here is that the lifecycle of these dependent
>> objects should be the same as the presenter's lifecycle.
>
> Hi Uwe, that is precisely our problem.
>
> I would like to have a FX (complex) component (FXML file, includes other
> FXML files, up to 3 levels) in a way that man presenter knows its
> sub-presenters (auto-wired, on creation), including lifecycle in a way
> that Uwe described.
but they do get pushed to the context so if you have
root.fxml (RootController)
+ header.fxml (HeaderController)
+ content.fxml (ContentController)
+ foot.xml (FooterController)
Then you can do in your RootController:
class RootController {
@Inject
@Optional
HeaderController header;
@Inject
@Optional
ContentController content;
@Inject
@Optional
FooterController footer;
}
you need to declare the stuff @Optional because initially they are NULL
or am I missing the point completely. Then please create a sample of
what you have in mind (even if it currently does not run and add that to
the bug)
Tom
|
|
| |
Re: Loading an FXML file and inject its controller into an E4 Part automatically [message #1385779 is a reply to message #1385776] |
Wed, 11 June 2014 03:59   |
Eclipse User |
|
|
|
I gets clearer but I'm not 100% sure I can suggest the prefered/nicest
way yet. Would you mind setting up a small sample project with the
information so that I can play around a bit with it and attach it to the
bug report.
I think the ideal thing would be to get away without any manual code -
I'm not sure why you need another class because you already have MVP
although FX calls it controller.
What are the responsibilities of the different classes:
- FXML - View
- Controller
- Presenter
Tom
On 11.06.14 09:41, Uwe San wrote:
> Hi Tom,
>
> obtaining the controller instances is not the problem. It works fine as
> you described. The problem is in the presenters (or any other classes
> that are not referenced in the FXML file or managed by E4 by default).
>
> Let's take your example and assume there is a presenter class associated
> with every controller:
>
> RootPresenter
> + HeaderPresenter
> + ContentPresenter
> + FooterPresenter
>
> The RootPresenter class could be an E4 part and look like this:
>
>
> public class RootPresenter {
>
> // field declarations...
>
> @PostConstruct
> public void init(BorderPane pane, @FXMLLoader FXMLLoaderFactory
> loaderFactory, HeaderPresenter headerPresenter) {
>
> Data<Node, RootController> data = loadFXML(loaderFactory);
> pane.setCenter(data.getNode());
>
> RootController rootController = data.getController();
> HeaderController headerController =
> rootController.getHeaderController();
> headerPresenter.setController(headerController);
> headerPresenter.init();
> // do more meaningful stuff with the sub-presenter or the
> controllers...
>
> }
> }
>
>
> Now the question is: what's the best way to put the HeaderPresenter
> instance into the part's context such that we can conveniently inject it
> into the init() method? The HeaderPresenter instance should have the
> same lifecycle as the part.
>
> Jacek mentioned things like context functions, object suppliers,
> @Creatable, ... and it would be helpful to know what the preferred
> mechanism is - if there is one.
>
> I hope the problem is clearer now.
>
> Thanks,
> Uwe
|
|
| | |
Goto Forum:
Current Time: Sat Apr 19 11:36:03 EDT 2025
Powered by FUDForum. Page generated in 0.25250 seconds
|