Integrating Spring with FXML nested controllers

Adam Granger adam at adamish.com
Fri Feb 27 06:28:16 UTC 2015


I'd be interested in the groups opinions...

From
http://stackoverflow.com/questions/28741472/integrating-spring-with-fxml-nested-controllers

I'm implementing a large application using JavaFX but unsure how to deal
with nested controllers and Spring.

 * The FXML has already been provided by the design team and has up to 3
levels of nested FXML using the include mechanism.

 * Models will be defined in Spring
 * I need to inject the models into all the nested controllers
 * There are multiple instances of controllers of the same type. i.e.
parts of the screen are identical but powered by different instances of
the same model type.

My work so far
--------------

 * I've read [Stephen Chin's blog - JavaFX in Spring Day 2 – Configuration
and FXML][1] and other SO questions however these only deals with top
level controllers.

 * I experimented with FXMLLoader.setControllerFactory() mechanism and
defining the controllers in the application context, however this only
gives the class of the controller to create, which means there is no way
to differentiate the two controllers of the same type but with different
data.

Problem with using controller factory:

    loader.setControllerFactory(new Callback<Class<?>, Object>() {
        @Override
        public Object call(Class<?> param) {
            // OK but doesn't work when multiple instances controller of
same type
            return context.getBean(param);
        }
    });

 * My best approach so far is for the top level controller to be wired up
using Stephen Chin approach.
   * For the case with multiple bean instances the parent controller will
get references to the specific beans via @Autowire/@Qualifer and then
set on the corresponding controller.
  * The next level of controllers can be wired up too by exposing them on
the top level controller and calling autowire()

Specific questions
------------------
 * Is there anyway of using controller factory mechanism so I can define
the controllers in the spring context instead so that it is easier to
wire them up ?
 * Is there some other method I can use?

Example
-------

Spring context

	<context:annotation-config />
	<bean id="modelA" class="org.example.Model">
		<property name="value" value="a value" />
	</bean>
	<bean id="modelB" class="org.example.Model">
		<property name="value" value="b value" />
	</bean>

Top level

    <HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"
minWidth="-Infinity" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.example.TopLevelController">
       <children>
          <TabPane tabClosingPolicy="UNAVAILABLE">
            <tabs>
              <Tab text="A">
                   <content>
                      <fx:include fx:id="a" source="nested.fxml" />
                   </content>
              </Tab>
              <Tab text="B">
                   <content>
                      <fx:include fx:id="b" source="nested.fxml" />
                   </content>
              </Tab>
            </tabs>
          </TabPane>
       </children>
    </HBox>

Nested level

    <HBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0"
prefWidth="200.0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.example.NestedController">
       <children>
          <TextField fx:id="value" />
       </children>
    </HBox>

Application main

    public class NestedControllersSpring extends Application {
        public static void main(String[] args) {
            launch(args);
        }
        @Override
        public void start(Stage stage) throws Exception {

            ClassPathXmlApplicationContext context =
                    new ClassPathXmlApplicationContext("/app-context.xml");

            FXMLLoader loader = new
FXMLLoader(getClass().getResource("/top-level.fxml"));
            stage.setScene(new Scene(loader.load()));
            TopLevelController top = loader.getController();
            context.getAutowireCapableBeanFactory().autowireBean(top);
            context.getAutowireCapableBeanFactory().autowireBean(top.getAController());
            context.getAutowireCapableBeanFactory().autowireBean(top.getBController());
            top.init(); // needed because autowire doesn't call
@PostConstruct
            stage.show();
        }
    }

Top level controller

    public class TopLevelController {

        @FXML
        private NestedController aController;

        @FXML
        private NestedController bController;

        @Autowired
        @Qualifier("modelA")
        private Model a;

        @Autowired
        @Qualifier("modelB")
        private Model b;

        @PostConstruct
        public void init() {
            aController.setModel(a);
            bController.setModel(b);
        }
        public NestedController getAController() {
            return aController;
        }
        public NestedController getBController() {
            return bController;
        }
    }

  [1]: http://steveonjava.com/javafx-in-spring-day-2/





More information about the openjfx-dev mailing list