FXML and Google Guice


JavaFX comes with the possibility to define your UI using a declarative XML-Syntax instead of writing Java code. To react on user input the XML-Syntax allows you to call back into programm code.

A simple example looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
	Do not edit this file it is generated by e(fx)clipse from /at/bestsolution/efxclipse/runtime/examples/guice/guice.fxgraph
-->


<?import java.lang.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane xmlns:fx="http://javafx.com/fxml" fx:controller="at...LoginController"> 
	<children>
		<Label layoutX="10" layoutY="10"> 
			<text>Username</text>
		</Label>
		<TextField fx:id="username" layoutX="80" layoutY="10"/> 
		<Label layoutX="10" layoutY="40"> 
			<text>Password</text>
		</Label>
		<PasswordField fx:id="password" layoutX="80" layoutY="40"/> 
		<Button layoutX="80" layoutY="70" onAction="#login"> 
			<text>Login</text>
		</Button>
		<Label fx:id="message" layoutX="80" layoutY="100"/> 
	</children>
</AnchorPane>

The controller:

package at.bestsolution.efxclipse.runtime.examples.guice.controller;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import at.bestsolution.efxclipse.runtime.examples.guice.service.ILoginService;

import com.google.inject.Inject;

public class LoginController {
	@FXML
	TextField username;
	
	@FXML
	PasswordField password;
	
	@FXML
	Label message;
	
	@FXML
	public void login(ActionEvent event) {
		// do the login
	}
}

And load the FXML-File like this:

FXMLLoader.load(
  GuiceExample.class.getResource("guice.fxml"), 
  null, 
  new JavaFXBuilderFactory()
);

If you are used to dependency injection – like I am since working on Eclipse 4 – you can’t imagine to work without it anymore and so you’d like to make guice inject an ILoginService to the controller.

package at.bestsolution.efxclipse.runtime.examples.guice.controller;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import at.bestsolution.efxclipse.runtime.examples.guice.service.ILoginService;

import com.google.inject.Inject;

public class LoginController {
	@Inject
	ILoginService loginService;
	
	@FXML
	TextField username;
	
	@FXML
	PasswordField password;
	
	@FXML
	Label message;
	
	@FXML
	public void login(ActionEvent event) {
		try {
			long id = loginService.login(username.getText(), password.getText());
			message.setStyle("-fx-text-fill: green;");
			message.setText("Logged in as User: " + id);
		} catch (IllegalArgumentException e) {
			message.setStyle("-fx-text-fill: red;");
			message.setText(e.getMessage());
		}
	}
}

Now the question is how to make the FXMLLoader use Guice to inject stuff. The problem is that you can’t pass the loader a delegate so that you can make Guice create the controller instance which means you can’t use constructor injection but that’s a minor problem for me.

So we let the FXMLLoader construct the controller and execute the injection afterwards:

public static <N> N loadFXML(Injector injector, URL url) throws IOException {
	FXMLLoader loader = new FXMLLoader();
	loader.setLocation(url);
	loader.setBuilderFactory(new JavaFXBuilderFactory());
	InputStream in = url.openStream();
	N value = (N) loader.load(in);
	in.close();
		
	if( loader.getController() != null ) {
		injector.injectMembers(loader.getController());
	}
		
	injectLoaders(loader, injector);
		
	return value;
}
	
private static void injectLoaders(FXMLLoader parentLoader, Injector injector) {
	for( FXMLLoader l : parentLoader.getIncludes() ) {
		if( l.getController() != null ) {
			injector.injectMembers(l.getController());
			injectLoaders(l, injector);
		}
	}
}

I’ve packaged up the code in my runtime modules so you can make use of it quite easy. A similar implementation is available for Eclipse-DI but Eclipse-DI requires an OSGi-Runtime whereas the guice one can be used with or without OSGi.

This entry was posted in e(fx)clipse. Bookmark the permalink.

6 Responses to FXML and Google Guice

  1. Zoom Club says:

    Can you tell me as a first time Eclipse user how to install e(fx)clipse as a remote site, thanks.

  2. Zoom Club says:

    Thanks, that worked fine. I have a few more new to Eclipse questions.

    In NetBeans it is possible to create JavaFX projects from templates and open samples. Is that possible in Eclipse? If not then do I start a JavaFX project with a normal Java project template and FXML with a normal XML template?

    Is there a way to package the templates and samples for NetBeans and install them in in Eclipse? Maybe just importing the NetBeans JavaFX samples is just as easy?

    • Tom Schindl says:

      Adding a project wizard is on my todo list for the 0.0.7 release. If I can find the time to fix some remaining CSS issues today and the wizard I’m going to release 0.0.7 today else I’m going to release it some time this week.

  3. Pingback: Java desktop links of the week, October 24 | Jonathan Giles

  4. Pingback: JavaFX links of the week, October 24 // JavaFX News, Demos and Insight // FX Experience

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.