vigoo / scalafxml

Bridging the gap between scalafx and FXML with generated proxies
94 stars 11 forks source link

Storing Instances via a Object Singleton #14

Closed mdedetrich closed 9 years ago

mdedetrich commented 9 years ago

Assuming you have a @sfxml controller defined like this

@sfxml
class SomeController(
    private val someTextField:TextField,
    private val dep:Dependency
) {
    // Do stuff here
    SomeControllerFactory.get = Option(this)
}

object SomeControllerFactory {
    var get:Option[SomeController] = None
}

The following generates a compile time error like this

[error] /Users/matthewdedetrich/example/src/main/scala/com/example/SomeController.scala:37: type mismatch;
[error]  found   : SomeController.this.Controller
[error]  required: com.example.SomeController
[error]   SomeControllerFactory.get = Option(this)

Using a companion object (i.e. object SomeController instead of object SomeControllerFactory) doesn't seem to work either, you would get the following

Compiling ScalaFXML proxy class for SomeController
[error] /Users/matthewdedetrich/example/src/main/scala/com/example/SomeController.scala:16: top-level class with companion can only expand into a block consisting in eponymous companions
[error] @sfxml

If you are wondering, I am trying to retrieve an store an Instance of SomeController (there is only going to be one instance of SomeController in my app) in a singleton object, so I can access it from outside of SomeController (i.e. some other controller does some action, and I want to access the instance of SomeController to do something to it)

vigoo commented 9 years ago

You can't use SomeController directly, because it is transformed to an inner class during the compilation. First you should define a trait SomeController implements and use that instead in your code:

trait SomeControllerInterface {
...
}

@sfxml
class SomeController extends SomeControllerInterface {
...
}

object SomeControllerFactory {
    var get:Option[SomeControllerInterface] = None
}

The real, generated controller class implements the ControllerAccessor trait. If you can get a reference to the controller, you can cast it to ControllerAccessor and get your SomeControllerInterface with

controller.as[SomeControllerInterface]

The FXMLLoader class has a helper function to do this, called getController.

For a complete example, see the GetControllerDemo.scala

mdedetrich commented 9 years ago

Thanks for the help, managed to get it to work by simple making it extend an interface. The FXMLLoader getController method didn't work, because the controller I was trying to reference was a nested controller (using fx:include), and I was getting a runtime error about the Root Controller being unable to cast to SomeControllerInterface

Simple using the

object SomeControllerInterface {
   var get:Option[SomeControllerInterface] =  None
}

method worked for me, and is probably more idiomatic scala code