tornadofx icon indicating copy to clipboard operation
tornadofx copied to clipboard

Support for creating components using constructor via dependency injection

Open TalosDx opened this issue 4 years ago • 5 comments

koin supports inject with a constructor, which is a good way to describe exactly what is needed without code bloat, especially when there are quite a few class properties

But tornadofx itself creates its components through an empty constructor, without using di container

init di container

FX.dicontainer = object : DIContainer, KoinComponent {
    override fun <T : Any> getInstance(type: KClass<T>): T =
        getKoin().get(clazz = type, qualifier = null, parameters = null)
}

example of view

class LoginView(windowController: WindowController, windowView: WindowView) : View("Auth") {
    override val root = vbox {
        windowView.root
    }
}

error

SEVERE: Uncaught error
java.lang.InstantiationException: dev.talosdx.pentaquark.view.auth.LoginView
	at java.lang.Class.newInstance(Class.java:427)
	at tornadofx.FXKt.find(FX.kt:434)
	at tornadofx.FXKt.find$default(FX.kt:423)
	at tornadofx.App.start(App.kt:83)

TalosDx avatar Jul 26 '20 21:07 TalosDx

I do not know all the subtleties, but I can suggest this option FX.kt

fun <T : Component> find(type: KClass<T>, scope: Scope = FX.defaultScope, params: Map<*, Any?>? = null): T {
...
val cmp = dicontainer?.getInstance(type) ?: type.java.newInstance()
...
}

can also modify the interface DiContainer by adding a default method

fun <T : Any> newInstance(type: KClass<T>): T = type.java.newInstance()

Where you really need to get a new object every time.

TalosDx avatar Jul 26 '20 22:07 TalosDx

I have code like this, instead of parameters in the constructor, you can use this:

class LineChart24Hours : Fragment() {
    val titleChart: String by param("") // default value
    val titleAxisY: String by param("")
    val tickUnitStart: Double by param(100.0)
    val lowerBound: Double by param(0.0)
    val upperBound: Double by param(0.0)
    val isAutoRanging: Boolean by param(true)
    val isForceZeroInRange: Boolean by param(false)
}

class ViewTestChar() : View() {
    val chart: LineChart24Hours by fragment(
        params = mapOf(
            LineChart24Hours::upperBound.name to 10000.0,
            LineChart24Hours::titleAxisY.name to "Title"
        )
    )

also why don't you want to inject controllers and view?

SchweinchenFuntik avatar Jul 27 '20 05:07 SchweinchenFuntik

Firstly, this is validation, when the compiler checks (in the case of koin) everything that is needed before starting the application.

Second is the speed and ease of writing code.

Thirdly, I can change di without any problems, since 90% supports inject via the constructor

Fourth, making such changes should not create problems.

Fifth, we get support for all the possibilities of di

And a thousand more reasons why

TalosDx avatar Jul 27 '20 11:07 TalosDx

if you need exactly koin support: https://edvin.gitbooks.io/tornadofx-guide/content/part2/Dependency_Injection.html

SchweinchenFuntik avatar Jul 28 '20 05:07 SchweinchenFuntik

Firstly, this is validation, when the compiler checks (in the case of koin) everything that is needed before starting the application.

my version is also checked in compile time.

Second is the speed and ease of writing code.

this is subjective, people may not use DI at all

Thirdly, I can change di without any problems, since 90% supports inject via the constructor

why change DI? Why not use native DI?

Fourth, making such changes should not create problems.

what problems exactly? How does your option solve them?

Fifth, we get support for all the possibilities of di

what for ?

I would add the ability to inject through the constructor in the native DI, this would really be useful

SchweinchenFuntik avatar Jul 28 '20 05:07 SchweinchenFuntik