2

I have an app in JavaFX, which has main scene with menu and toolbar, and smaller scenes, which are injected into this main scene, after one of menu buttons are being pressed.

Now, HomeCntroller is responsible for either scene components: Home Scene (with toolbar and menu), and injected scene. This leads me to create massive, huge and very unprofessional controller if number of injected scenes is more than one.

How to split controller responsibility?

Now my Controller looks like this: changeDashboardPane method injects smaller Pane into my main HomePane.

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired) )
public class HomeController extends AbstractController {
    private static final Logger LOG = Logger.getLogger(HomeController.class);

    private final BudgetProfileService budgetProfileService;

    @FXML
    private Label usernameLabel;

    @FXML
    private ComboBox<String> budgetProfilesComboBox; 

    @FXML
    private AnchorPane dashBoardPane;

    @FXML
    public void initialize() {
        refreshUsernameLabel();
        getAllBudgetProfiles();
        changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
    }

    private void refreshUsernameLabel() {
        String username = UserAccountProvider.getLoggedUser().getUsername();
        usernameLabel.setText(username);
    }

    private void getAllBudgetProfiles() {
        List<String> budgetProfileNames = budgetProfileService.getAllBudgetProfileNames();
        if (!budgetProfileNames.isEmpty()) {
            budgetProfilesComboBox.getItems().clear();
            budgetProfilesComboBox.getItems().addAll(budgetProfileNames);
        }
    }

    @FXML
    public void handleFinancesButtonAction() {
        changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
    }

    @FXML
    public void handlePeriodButtonAction() {
        changeDashboardPane(PaneFactoryKeys.PERIOD_PANE);
    }

    @FXML
    public void handleStatisticsButtonAction() {
        changeDashboardPane(PaneFactoryKeys.STATISTICS_PANE);
    }

    @FXML
    public void handleSettingsButtonAction() {
        changeDashboardPane(PaneFactoryKeys.SETTINGS_PANE);
    }

    private final void changeDashboardPane(String paneFactoryKey) {
        double injectedPanePosition = 0.0;
        Pane paneToChange = getPaneFromFactory(paneFactoryKey);
        dashBoardPane.getChildren().clear();
        AnchorPane.setTopAnchor(paneToChange, injectedPanePosition);
        dashBoardPane.getChildren().add(paneToChange);
    }

}

To get this more clear, screens:

without injected second pane with injected second pane

Any ideas guys?

G.Spansky
  • 852
  • 6
  • 17
  • 32
  • 1
    Have a global controller that passes control to a controller specified for each pane is my first thought. Though this looks fine to me. – arynaq Sep 28 '16 at 20:27
  • Yeah, but if I create controllers for injected panes, JavaFX won't inject smaller pane components inside this controller. So I have to create something like component registry for buttons, labels, inputs... – G.Spansky Sep 28 '16 at 20:30
  • The problem is, that JavaFX Scene can only have one controller. – G.Spansky Sep 28 '16 at 20:30
  • 1
    You can insert other FXML elements into your main fxml and keep the controllers separate. I think this may be what you want to do. Have you looked at http://stackoverflow.com/questions/19342259/how-to-create-multiple-javafx-controllers-with-different-fxml-files? – Hypnic Jerk Sep 28 '16 at 20:40
  • 1
    There should be exactly one controller class for each FXML file. So if you need to split it into smaller components, split the view into smaller FXML files and create a controller class for each. Use `` etc. – James_D Sep 28 '16 at 20:44
  • 1
    Ok, I found out, that any pane, that I can load from FXML can have its own Controller, even if that pane is injected into another pane. This resolve my problem :) Thank you guys for answers! :) – G.Spansky Sep 28 '16 at 20:45
  • 1
    It **must** have its own controller – James_D Sep 28 '16 at 20:46

3 Answers3

4

I would recommend you to divide your main scene in smaller ones, for example you can have a tools scene, a header scene, a content scene and so on. Then you should have one controller for every scene.

After that I would use a publisher-subscriber pattern to deal with behaviors, like when you press a button on settings scene, it triggers an event that other scenes listen to and then they handle it changing their state accordingly.

I hope it was clear and can help!

Johan Duke
  • 199
  • 6
  • Yes, I have HomeScene, with empty dashboard space, and smaller panes that being injected into HomeScene. Now each pane has its own controller, and everything is great :) – G.Spansky Sep 28 '16 at 21:05
1

Create multiple controllers , multiple FXML files - to continue on my answer that i provided you before, JavaFX how to inject new FXML content to current Scene each of those views that have separate fxml file also has

fx:controller="appplication.ExampleViewController"

attached to it.So what you do is create main controller as was mentioned , that is basically the FRAME CONTAINER that encapsulates controls to change your dynamic container.If your application is really ui rich and have a lot of functionality in one controller , you can break down your view even further:

For instance take out menu and put it into separated controller , and insert it into your main view with main controller

/same way as in method setView()/

, what you are doing is just taking it away to keep controller code smaller, YOU DONT DECREASE/INCREASE SCENE GRAPH THIS WAY, doesnt have a drawback its just a personal preference.

You gonna end up with more fxml files and controllers in the end.Its all the same thing as from your previous question there is no additional code needed you can actually reuse what was already provided.

Data between controllers are passed thru MODEL. - look more into MVC dont work with application data in controllers only care about view or passing them from/into model

Community
  • 1
  • 1
Tomas Bisciak
  • 2,801
  • 5
  • 33
  • 57
1

To avoid a huge contoller class, as I am using multiple tabs, I split the tabs to single java files.

My solution was to create a cascade of classes:

  • Base: Containing all defs for FX types
  • Tab1 extends Base: Tab one implementation
  • Tab2 extends Tab1: Tab two implementation
  • Controller extends Tab2 implements Initializable: Implements initialize(URL url, ResourceBundle resourceBundle)

Important: Any accessed object must be definded in the current tab or before. Any Objects in Base are available in Controller whereas no object of Controller is accessable in Base, Tab1 or Tab2.

Feel free to add your opinion as comment or submit a improvement.

User8461
  • 366
  • 2
  • 14