Search code examples
scalamodel-view-controllerfxmlscalafx

Passing information between two stages with ScalaFX fails due to not setting the field in controller


I am trying to understand the message passing possibilities of ScalaFX in combination with ScalaFXML. I created a small example, which has two views with controllers defined in the FXML. The first (or Main) view, is supposed to send a String to the second (or Dialog) view, by a button press.

I followed the example of the ScalaFXML Github page and used a trait in order to get the controller and it's possible for me to call a method on the Dialog controller, which sets a field to different value. This method gets called correctly, but the field isn't overwritten, when I check by clicking a button. Does this have something to do with ScalaFXML injecting the Controller?

Bellow I have the code for the two controllers

@sfxml
class MainController(val clicky: Button,
                     val inputText: TextField,
                     val resultText: TextArea) {

  /** Creates a new window (in addition to the main window) and passes the contents of the text view to this window */
  def onButtonPressed(): Unit = {

    // Create the new window
    val dialogFXML: String = "/Dialog.fxml"
    val resource = getClass.getResource(dialogFXML)
    val rootView = FXMLView(resource, NoDependencyResolver)

    val loader = new FXMLLoader(resource, NoDependencyResolver)
    loader.load()
    val controller = loader.getController[AmountReceiver]
    controller.setAmount(inputText.text.value)

    val dialog = new Stage() {
      title = "Add Person"
      scene = new Scene(rootView)
      resizable = false
      initModality(Modality.ApplicationModal)
    }

    if (!dialog.showing()) {
      dialog.show()
    } else {
      dialog.requestFocus()
    }
  }
}

and

@sfxml
class DialogController(val minAmountOfWords: Label,
                       val sendToMainButton: Button,
                       val input: TextArea) extends AmountReceiver {

  var amount: String = "empty"

  def submitText(): Unit = {
    println(s"The actual amount is ${this.amount}")

    // Close Dialog
    quitDialog()
  }

  override def setAmount(amount: String): Unit = {
    this.amount = amount
    println(s"Setting the amount to ${this.amount}")
  }


  def quitDialog(): Unit = {
    val stage: Stage = input.getScene.getWindow.asInstanceOf[Stage]
    stage.close()
  }

}

Running this code and entering "2" in the textfield, will print the following output:

Setting the amount to 2
The actual amount is empty

Solution

  • I actually figured it out myself. The problem lays within the rootView, which is received by creating a new FXMLView. In order access the correct controller, the rootView has to be received by the loader, which is already used to get the controller.

    So calling

    val rootView = loader.getRoot[jfxs.Parent]
    

    instead of

    val rootView = FXMLView(resource, NoDependencyResolver)
    

    Like in the above example, the rootView is passed to the Scene constructor.