-2

I am creating a popup with a date picker and some other nodes. I want this date picker to show the current date when the popup is opened.

So, I set the current date in the popup controller constructor, just like I do everywhere else in my application:

popup constructor

 public TimeAndDateController(Stage stage, Task task) {
        try {
            this.taskReference = task;
            this.popupStage = stage;

            this.datePicker = new DatePicker();
            datePicker.setValue(LocalDate.now());
            

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

I want the popup to appear when the user clicks a button in the main application window, so I add all the necessary components, and finally call showAndWait() on the popup stage.

Event firing up the popup, somewhere in main application's controller

customTime.setOnAction(e -> Platform.runLater(() -> {

    Task task = getSelectedTask();

    try {


        FXMLLoader loader = Utility.getFXMLLoader("fxml/timeAndDatePopup.fxml");

        Stage popupStage = new Stage();

        loader.setControllerFactory(c -> TimeAndDateControllerFactory(popupStage, task));

        Scene scene = new Scene(loader.load());

        popupStage.setScene(scene);

        popupStage.showAndWait();

    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }

}));

Now the popup appears as expected without any error, but the data picker (or any other node I have there) has no value.

I don't understand why this is happening. All nodes are initialized, there are no null pointers or error messages, FXML's fine, yet I can't set a value or have any control over the nodes. I spent hours today on this, looking for answers and found nothing.

So far I have tried:

  • Using a Popup class instead of a stage (as shown here).
  • Setting the popup stage inside the popup controller, but that led to another oddity that prevented me from closing the popup using stage.close().
  • Various methods for loading FXML and manually setting the controller, eg.
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
0tto
  • 47
  • 1
  • 9
  • 3
    Is `this.datePicker` injected by the `FXMLLoader` perchance? If so, then it is replaced by a different `DatePicker` instance, making what you do in the constructor ineffective; to fix this, save the date in a field and set the `DatePicker`'s value in the `initialize` method. In general, you should _never_ manually initialize an FXML-injected field. – Slaw Jul 11 '22 at 23:45
  • 2
    Post a [mre]. That is, create a minimal example that reproduces the problem and then actually copy and paste the complete code here so others can reproduce the issue. The snippets you have include obvious typos (unless you have some really weird methods you haven’t included) and you’re really forcing people to guess what you real code looks like to try and diagnose the problem. – James_D Jul 12 '22 at 00:04
  • Aside: If your goal is to get a `LocalDate` instance based on user input via a popup, then consider using a `javafx.scene.control.Dialog`. – Slaw Jul 12 '22 at 00:06
  • You are right. One of the first things I did, was implementing the `Initializable` interface, but it didn't work for some reason so I dropped it at some point. Now it works like a charm. 8 hours of debugging went to waste. Well fuck me. Nonetheless, thank you very much. Somehow it didn't occurred to me that, loading FXML also replaces nodes with new instances. – 0tto Jul 12 '22 at 00:10
  • @0tto *”Somehow it didn’t occur to me that loading the FXML also replaces nodes with new instances.”* This is a bit inaccurate. It doesn’t really “replace nodes”. The FXML loader creates new nodes and if they have `fx:id`s it assigns the corresponding references to those nodes. I’m genuinely interested as to how you actually thought it did work. The FXML loader is just a Java class like any other, and while the actual implementation is complex, what it’s doing is not hard to understand. Understanding what the classes you are using are doing will avoid wasting hours on debugging. – James_D Jul 12 '22 at 00:18
  • @James_D I get your point, but to be frank I was just messing around with JavaFX, trying to make a simple app and see how it turns out. The other thing is that: **a)** I don't really have time or willingness too study every class I use and **b)** this is not really the point, is it? I mean the whole idea of class or a method is to present the programmer with certain abstraction - I don't really care how exactly given method works, as long as it works. Way I see it: FXMLLoader loads FXMLs ;]. I absolutely get that this not applicable to all situations (like this one)... But it kinda should. – 0tto Jul 12 '22 at 00:32
  • 1
    @0tto But it clearly does more than “load FXMLs”; it connects to a controller. I’m not asking you to understand “how it works” in the sense of “how it’s implemented” but in the sense of “what it does” (perhaps my previous comment was poorly worded). What’s the abstraction in terms of how the loaded FXML relates to the controller? You can’t really use an FXML Loader and a controller without understanding that. And once you’re more than an hour into debugging, surely at that point you’d delve into that before going to all the trouble of posting here? – James_D Jul 12 '22 at 00:33
  • @James_D That's mostly right - but then again, I'm not really serious about that JavaFX thing. It's a side project for me, and my time's limited. Perhaps I should've taken a dive into documentation, I can agree on that. I didn't do that because it didn't occurred to me, that Loader might be the problem. Also I admit I tend to avoid Java's documentation 'cuz it sucks, when compared to .NET's docs. All I can do now is draw conclusions and move on. – 0tto Jul 12 '22 at 00:57

1 Answers1

0

As the gentlemen @Slaw and @James_D in the comments rightly pointed out, the solution to the problem is the implementation of the initializable interface, and initialize method, in which all properties of nodes ought to be set.

This is because, as it turns out:

The FXML loader creates new nodes and if they have fx:ids it assigns the corresponding references to those nodes.

Rookie mistake.

So, in this case, the correct way to set date picker's value is as follows:

 @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
         datePicker.setValue(LocalDate.now()); // works!
    }

0tto
  • 47
  • 1
  • 9
  • _As the comments rightly pointed out, the solution to the problem is the implementation of the initializable interface_ that's not what they said ;) – kleopatra Jul 12 '22 at 03:54