# JavaFx - Spring DependencyInjection with fx:include



## Hesk (20. Jan 2014)

Hello!

Ich hab ein main.fxml wo mittels <fx:include source="menubar.fxml" /> die Menübar reingeladen wird.
Diese menubar hat einen eigenen Controller. Doch leider kann ich diese Controller mit Spring nicht injecten.

Durch fx:include wird scheinbar eine neue Instanz von dem Controller gemacht.

Wie kann ich das lösen?

danke, lg


----------



## dzim (20. Jan 2014)

Ich lehne mich vielleicht aus dem Fenster, aber im "normalen" JavaFX, glaube ich, gibt es zwe Wege, den Controller festzulegen. Der erste Weg ist über das FXML (das finden einige doof, da man der Deklaration sagt, was sein Controller ist - viele finden den Weg anders herum besser: Hab deinen Controller und sag ihm, was die UI ist). Der zweite weg ist über den FXMLLoader. Wenn du eine neue Instanz anlegst (nicht die statische Methode verwendest), kannst du dieser einen Controller für ein FXML zuweisen - hier könntest du nun vielleicht mit deiner Spring-DI arbeiten.

Hilft das?


----------



## Hesk (21. Jan 2014)

Nein. Leider ist es mir nicht so ganz klar.

Könntest du vielleicht ein Bsp. geben?


----------



## Hesk (21. Jan 2014)

So. Habs nun gelöst. Für alle welche das selbe Problem haben:

Es gibt das main.fxml welche das menubar.fxml inclucded. Damit dann im MenubarController mittel Spring-Autowire beans injected werden können, muss dem FXMLLoader ein ControllerFactory gesetzt werden.

Start des Programms:


```
private AnnotationConfigApplicationContext context;
	
    @Override
	public void stop() throws Exception 
	{
    	if (context != null)
    	{
    		context.close();
    	}
    	
		super.stop();
	}

	@Override
    public void start(Stage primaryStage) throws Exception 
    {
    	context = new AnnotationConfigApplicationContext(SpringFactory.class);
    	MainController mainController = context.getBean(MainController.class);
    	mainController.init(primaryStage, context);
    	mainController.showMainView();
    }
 
    public static void main(String[] args) 
    {
        launch(args);
    }
```

Methode in mainController:


```
public void init(Stage primaryStage, final ApplicationContext context)
{

	try 
        {
            primaryStage.getIcons().add(new Image("file:resources/images/windowIcon.jpg"));
            primaryStage.setTitle("...");
            
            InputStream fxmlStream = null;
            try
            {
                fxmlStream = getClass().getResourceAsStream(StaticConfig.FXML_MAIN);
                FXMLLoader loader = new   FXMLLoader(getClass().getResource(StaticConfig.FXML_FOLDER));
                
                loader.setControllerFactory(new Callback<Class<?>, Object>() 
                {
                	@Override
                	public Object call(Class<?> clazz) 
                	{
                		return context.getBean(clazz);
                	}
                });
                
                loader.load(fxmlStream);
                Scene myScene = new Scene(((MainController)loader.getController()).getView());
                primaryStage.setScene(myScene);
            }
            finally
            {
                if (fxmlStream != null)
                {
                    fxmlStream.close();
                }
            }
            
            
        } 
        catch (Exception e) 
        { 
            Dialogs.showErrorDialog(primaryStage,
            						"Coult not load main Fxml: " + StaticConfig.FXML_MAIN,
        							"MainView", "Error", e);
        }

}
```


----------



## dzim (21. Jan 2014)

1)
Ok, anscheinend war es nicht deutlich genug, aber deine Lösung geht haargenau in die Richtung, die ich auch vorgeschlagen habe.

2)

```
FXMLLoader loader = new FXMLLoader(getClass().getResource(StaticConfig.FXML_FOLDER));
```
== 
	
	
	
	





```
FXMLLoader loader = new FXMLLoader(); loader.load(getClass().getResource("layout/Configuration.fxml").openStream());
```
== 
	
	
	
	





```
FXMLLoader loader = new FXMLLoader(); loader.load(getClass().getResourceAsStream("layout/Configuration.fxml"));
```
== 
	
	
	
	





```
FXMLLoader loader = new FXMLLoader(); loader.load(fxmlStream);
```

Du kannst dich dafür entscheiden, die FXML entweder im Konstruktor, oder über load anzugeben. Was du machst ist "doppelt gemoppelt" - sozusagen. Nicht schlimm, aber auch nicht notwendig.

3)
Ich kenne deine FXML nicht, ich vermute aber, dass dein Controller im FXML selbst steht, also irgend etwas wie 
	
	
	
	





```
fx:controller="my.package.MainController"
```
. Dadurch kannst du dich mit deiner ControllerFactory dazwischen hängen (bzw. musst es auf diese Art).

Ich habe vorgeschlagen, dass du die deklarative UI in _Unkenntnis_ über ihren späteren Controller lässt (der Weg, wie ihn z.B. Android geht: Dort wird die Actity oder ein Fragment so aufgebaut, dass es die deklarative UI (auch in XML-Form) lädt - das XML aber kennt die Activity nicht vorab).

Wenn du also den 
	
	
	
	





```
fx:controller="my.package.MainController"
```
-Teil weg lässt, kannst du im FXMLoader stattdessen einfach 
	
	
	
	





```
loader.setController(context.getBean(MainController.class));
```
 angeben.

Fazit:
Gemeinsam mit der Ersparnis aus 2) kannst mit 3) _acht_ oder mehr Zeilen sparen.
War das nun verständlicher?


----------



## Hesk (22. Jan 2014)

Ja, war verständlicher. 
Es löst aber mein Hauptproblem nicht.

Wenn ich es wie du mache und den Controller im FXML weg lasse, ihn dann mittels loader.setController(context.getBean(MainController.class)); setze, wie kann ich dem 2ten-FXML (welches im mainFXML included wird) einen Controller zuweisen?


----------



## dzim (22. Jan 2014)

Ah! Ich sehe, woher der Wind weht. Ja du hast recht, daran habe ich nicht gedacht. Und da ich es noch nie auf diese Art probiert hab...
Wenn ich so eine Art von UI-Dependency (kann man das überhaupt so nennen???) habe, dann bau ich das immer im Controller auf. Also in deinem Fall würde im Controller der Hauptseite, das FXML des Menüs laden und einfügen. Ich behaupte dabei aber nicht, dass das der bessere Weg wäre.
Was ich mich nur aus purer Neugier frage, ist: Wenn du keinen Controller angibst, kannst du dann die @FXML-Annotationen einfach im Controller der Hauptseite machen? Vielleicht ginge das ja. Dann hättest du halt einen Controller für zwei UIs (ob das dann allerdings noch im Sinne des Erfinders ist, weiß ich wiederum auch nicht).


----------



## Hesk (22. Jan 2014)

Ich möchte einen Controller pro FXML haben

So wie ich es jetzt habe funktioniert es ganz gut.


----------



## dzim (22. Jan 2014)

War auch nur ne Idee ;-)


----------

