Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Communication between two JavaFx controllers

I made a structure to of Controllers and Views (fxml) to separate my code as much as I could, and I'm wondering how to communicate between 2 controllers. I mean, a controller have to call some functions of another controller to set it up to date.

I think a schema of my current structure will be more explicit:

          Controller 1
           /              \
   fx:include    fx:include
       /                     \
Controller2      Controller3

Each controller has is own fxml view.
- Controller 1 : a container controller which has a TabPane element with 2 tabs (each tab correspond to 1 controller)
- Controller 2 : a list
- Controller 3 : a form

You've probably guessed that I want my form (controller 3) to automatically update my list (controller 2). For the moment, the form is only a "creation form", so I just want to add row in my list.

I've already tried to get my Controller 2 with FXMLoader and call the functions to relaod my tableView, no success..

Controller 1 (.java + .fxml) :

package pappu.controllers;

import pappu.core.controller.AbstractController;

public class FolderController extends AbstractController
{

}


<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox fx:id="view" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="pappu.controllers.FolderController">
  <TabPane>
    <tabs>
      <Tab text="RECHERCHE">
        <content>
          <AnchorPane id="Content">
            <children>
                <fx:include source="FolderList.fxml" />  
            </children>
          </AnchorPane>
        </content>
      </Tab>
      <Tab text="DOSSIER">
        <content>
          <AnchorPane id="Content">
            <children>
                <fx:include source="FolderFormAdd.fxml" />  
            </children>
          </AnchorPane>
        </content>
      </Tab>
    </tabs>
  </TabPane>
</VBox>

Controller 2 (.java + .fxml) :

package pappu.controllers;

import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;

import org.hibernate.Session;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.util.Callback;
import pappu.core.controller.AbstractController;
import pappu.entities.Folder;

public class FolderListController extends AbstractController implements Initializable
{
    /**
     * TableView object
     */
    @FXML private TableView<Folder> foldersTableView;

    /**
     * FolderNumber column object
     */
    @FXML private TableColumn<Folder, String> colFolderNumber;

    /**
     * Person column object
     */
    @FXML private TableColumn<Folder, String> colPerson;

    /**
     * Birthday date column object
     */
    @FXML private TableColumn<Folder, Date> colBirthdayDate;

    /**
     * List of folders
     */
    private static List<Folder> foldersList;

    /**
     * Constructor
     * Will make a call to initializeFoldersList()
     */
    public FolderListController()
    {
        initializeFoldersList();
    }


    /**
     * Initialize implementation of the Initializable interface
     * 
     * @param location
     * @param resources
     */
    @Override 
    public void initialize(URL location, ResourceBundle resources) 
    {
        initializeTableColumns();
        loadData();
    }

    /**
     * Query the database to retrieve the folder list
     */
    @SuppressWarnings("unchecked") 
    public void initializeFoldersList()
    {
        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();
        foldersList = session.createQuery("from Folder").list();
        session.close();
    }

    /**
     * Initialize columns binding to folders properties
     */
    public void initializeTableColumns()
    {
        colFolderNumber.setCellValueFactory(
                  new PropertyValueFactory<Folder,String>("folderNumber")
                      );
        colPerson.setCellValueFactory(
                new Callback<CellDataFeatures<Folder, String>, ObservableValue<String>>() {
                     public ObservableValue<String> call(CellDataFeatures<Folder, String> p) {
                         return new SimpleStringProperty(p.getValue().getFirstName() + " " + p.getValue().getLastName());
                     }}
          );
        colBirthdayDate.setCellValueFactory(
                  new PropertyValueFactory<Folder,Date>("birthdayDate")
                      );

    }

    /**
     * Put the folders list in the TableView object
     */
    public void loadData()
    {   
        ObservableList<Folder> listFold = FXCollections.observableArrayList(foldersList);       
        foldersTableView.setItems(listFold);
    }   
}


<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Label?>


<VBox fx:id="view" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="pappu.controllers.FolderListController">
    <Label fx:id="lblTest"></Label>
    <TableView fx:id="foldersTableView">
        <columns>
            <TableColumn prefWidth="75.0" text="N°" fx:id="colFolderNumber">
            </TableColumn>
            <TableColumn prefWidth="75.0" text="Personne" fx:id="colPerson">
            </TableColumn>
            <TableColumn prefWidth="75.0" text="Date de naissance" fx:id="colBirthdayDate">
            </TableColumn>
        </columns>
    </TableView>
</VBox>

Controller 3 (.java + .fxml) :

package pappu.controllers;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import org.hibernate.Session;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import pappu.core.AppFactory;
import pappu.core.controller.AbstractController;
import pappu.entities.Folder;
import pappu.entities.Gender;

public class FolderFormAddController extends AbstractController
{   
    @FXML TextField folderNumber;
    @FXML TextField firstName;
    @FXML TextField lastName;
    public void submitForm() throws IOException
    {   
        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();

        Folder folder = new Folder();

        folder.setFolderNumber(folderNumber.getText());
        folder.setFirstName(firstName.getText());
        folder.setLastName(lastName.getText());
        folder.setGender(Gender.m);

        session.save(folder);
        session.getTransaction().commit();
            // This doesn't work.. even tried with a simple Label
        AppFactory app = new AppFactory();
        FolderListController flc = app.folderListController();
        flc.initializeFoldersList();
        flc.loadData();
    }
}


<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox fx:id="view" prefHeight="216.0" prefWidth="421.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="pappu.controllers.FolderFormAddController">
  <children>
    <Label prefHeight="26.0" prefWidth="102.0" text="Numéro de dossier" />
    <TextField prefWidth="200.0" fx:id="folderNumber"/>
    <Label text="Prénom" />
    <TextField prefWidth="200.0" fx:id="firstName"/>
    <Label text="Nom" />
    <TextField prefWidth="200.0" fx:id="lastName"/>
    <Button mnemonicParsing="false" onAction="#submitForm" text="Enregistrer" />
  </children>
</VBox>

Precisions:
I made my application on this base: http://www.zenjava.com/2011/10/25/views-within-views-controllers-within-controllers/ and I use JavaFX 2 on Java JDK 7

I feel something missing in global functioning of a JavaFX application.

like image 253
jbrtrnd Avatar asked Mar 04 '14 17:03

jbrtrnd


1 Answers

Two ways come into my mind:

  1. Based on the "Nested Controllers" section of "Introduction to FXML" (link), you could inject the children controllers (2 & 3) into the parent (1) and have the parent coordinate their interactions:

    FXML (1):

    <fx:include source="FolderList.fxml" fx:id="list" />
    ...
    <fx:include source="FolderFormAdd.fxml" fx:id="addForm" />
    

    Java (1) (beware the names of the fields; must match <fx:id>Controller, i.e.):

    public class FolderController extends AbstractController {
        @FXML private FolderListController listController;
        @FXML private FolderFormAddController addFormController;
        void initialize() {
            // add code to coordinate them
        }
    }
    

    I do not like this solution much for this case, as it leads to strong coupling between the components. On the other hand, it may be the quickest.

  2. Use an event bus (e.g. from Google Guava). This can actually decouple your logic (e.g. the list component listens to the PersonAdded event, no matter how it was created; the form generates this event, without caring who is listening - if any). I think I would prefer this solution in your case. The event bus can optionally be retrieved using dependency injection.

Check out the answer pointed by the comment from jewelsea, it is great - I have already upvoted it myself :)

like image 131
Nikos Paraskevopoulos Avatar answered Sep 28 '22 15:09

Nikos Paraskevopoulos