0

I have a parent controller say HomeController It has a node SidePanel (JFXDrawer) with SidePanelController and a node anchorPane with varying controller.

     HomeController
          |
         / \
        /   \
       /     \
 anchorPane  SidePanel
(Controller   (Controller = SidePanelController)
 = varies)

The anchorPane node should load multiple fxml views with menu buttons clicked from SidePanelController. The problem here is in SidePanelController since the buttons are inside it, I cannot directly load onanchorPane as for SidePanelController the node anchorPane does not exists

This question seems duplicate of this but its not because the parent controller is waiting for scene to close so it fetches back the data to parent controller. But in my case, every time I click on menu button, it will load a view accordingly.
Can anybody provide resources for making controller for JFXDrawer (as child node).

If say, I have a side navigation drawer sidepanel.fxml like this

enter image description here

And I have a HomeScreen like this enter image description here

So by using following code, I stacked drawer in my homecontroller

try {
            SidePanelController controller = new SidePanelController();
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/sidepanel.fxml"));
            loader.setController(controller);
            Parent root = loader.load();
            drawer.setSidePane(root);
        } catch (IOException err) {
            err.printStackTrace();
        }

Finally, I will be getting this as combined output

enter image description here

Now what I want is whenever I try to click on say Lorry Receipt button on Side Navigation Panel, it should trigger the node residing on Parent controller. Even the event which will pass data back to parent controller without closing the child node will work.

Sushant
  • 165
  • 7
  • Does this answer your question? [Applying MVC With JavaFx](https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx) – SedJ601 Nov 13 '19 at 15:11
  • @Sedrick The link is somewhat relevant. But still it won't answer my question. I can simplify my question more like this. How can we set an event on a child node button which will trigger a parent variable or node? – Sushant Nov 13 '19 at 15:25
  • If you structure your project using these model ideas, you will not have the problem you currently have. – SedJ601 Nov 13 '19 at 15:38
  • The other option is https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml – SedJ601 Nov 13 '19 at 15:43
  • @Sedrick The second link you given is example of setting values to child controller from parent controller. What I wanted is fetching values from child controller back to parent controller so that parent controller processes it and acts accordingly. I am sorry, I am not able to re-frame my questions properly. – Sushant Nov 13 '19 at 15:49
  • There is nothing wrong with your question. The problem knowing how to use these code ideas to fix your problem. Both will fix your problem. – SedJ601 Nov 13 '19 at 15:51
  • Using the second idea, you can do something like `controller.getFirstButton().setOnAction(...)` after `drawer.setSidePane(root);`. – SedJ601 Nov 13 '19 at 15:54
  • To get the best help, you need to create a watered-down program that duplicates your problem. One with two buttons that should load two very basic FXML(FXMLs with just a label showing which one is loaded). – SedJ601 Nov 13 '19 at 16:02
  • @Sedrick After some trial and errors I found a solution. It might be non-ethical but works like charm though. As you suggested controller.getFirstButton(), I created event for every button on parent controller. – Sushant Nov 14 '19 at 10:03

1 Answers1

0

As @Sedrick suggested, I initialized events on all buttons of SidePanelController in HomeController (Parent) itself. At first it returned NPE, but later I let the buttons initialize and then fetch it back to parent controller. So here is the solution. It might be non-ethical, so other alternatives still appreciated.

public class SidePanelController implements Initializable {
    @FXML
    private JFXButton btn_lr;
    @FXML
    private JFXButton btn_shipment;
    @FXML
    private JFXButton btn_add_inward;
}

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
   //Your other activities when sidedrawer/pane initializes
}

//After I initialize, I would like all of these buttons to be fetched to my Parent controller. So instead of me passing each button separately, I made a list to save my time.
public ObservableList<JFXButton> fetchAllButtons(){
        ObservableList<JFXButton> allbuttons = FXCollections.observableArrayList(btn_lr, btn_add_inward, btn_shipment);
        return allbuttons;
}

Now in HomeController or ParentController, I fetch all these buttons and create events for it separately. So here goes the code.

public class HomeController implements Initializable {
    @FXML
    private JFXHamburger hamburger;
    @FXML
    private JFXDrawer drawer;
    //Creating Hamburger Task to toggle sidebar
    HamburgerBackArrowBasicTransition burgerTask;

    //Declaring sidepanelcontroller globally because I need it in multiple methods.
    SidePanelController controller = new SidePanelController();

    //Finally a list to fetch the list of all buttons from SidePanelController
    ObservableList<JFXButton> sidebuttons;

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
    //Using hamburger to toggle my side drawer
    burgerTask = new HamburgerBackArrowBasicTransition(hamburger);

    //Initializing the scene of drawer/SidePanel FXML file on Home.fxml or parent fxml itself 
    try {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/sidepanel.fxml"));
        loader.setController(controller);
        Parent root = loader.load();
        drawer.setSidePane(root);
        sidebuttons = controller.fetchAllButtons();
        //Make sure not to declare this before initializing or else it returns NPE 
        } catch (Exception err) {
            err.printStackTrace();
        }

        //Finally you can declare your tasks for each button by declaring events on each of those buttons. I thought only common but different thing you can between buttons is the name of it. So I used switch statement just to point out each button separately. (I know this might be unethical, but works for me though)
    for (JFXButton button: sidebuttons) {
         switch (button.getText()){
             case "Lorry Receipt":
             button.setOnAction(actionEvent -> {
                       //Your actions here when button with name Lorry Receipt is pressed
             });
             break;
             case "Shipment Memo":
             button.setOnAction(actionEvent -> {
                    //Your actions when button with name shipment memo is pressed
             });
             break;
             case "Inward Challan":
             button.setOnAction(actionEvent -> {
                   //Your actions when button with name Inward Challan is pressed
             });
             break;
             }
       }
   }
}

Other Advantage I found with this is, I don't have to show ProgressBar/ProgressIndicator of each scene separately. Since Child Component's ActionEvent is on ParentNode, the ProgressBar/Indicator can be binded to it and works like a charm.

Sushant
  • 165
  • 7