Is there a way to put fritz2 components in a class...
# fritz2
j
Is there a way to put fritz2 components in a class and let koin create the instances? I have the issue that I want to use koin for dependency injection. So, i can inject things like stores, services, and other things in render functions without relying on passing things around via function arguments, doing manual koin lookups inside the render function, or worse using global variables (just no).
b
Global koin with store instance lookups from within renders?
Or have your render functions act as koin factories with tags returning Unit
c
Global koin with store instance lookups from within renders?
That seems to be some kind of service locator? (which is considered an anti-pattern nowadays, isn't it?) @Jilles van Gurp If you have a deeper look at our components, they consist more or less of some factory functions, which almost always just accept different kind of parameters or sometimes does some kind of pre-configuration (``clickButton`` for example). You could try to ignore those factory functions and focus on the ``component``-classes! You could quite easily create instances of those manually via constructor injection, so I assume without knowing koin, that it should be possible to create those objects by some injector framework. There is only one mental problem I have currently: Our components and the whole rest of the framework emphasizes a declarative approach. Thus the reading is from outside to the inside level of UI structures (which reflects the object structure as well). If you model this with DI, you need the most inner objects first, so the reading gets reverted. I have some doubts, that this pays out in the end of the day concerning readability...
j
I consider global variables an all time anti pattern. Explains a lot about the state of frontend javascript. Dependency injection is completely normal on Android and java/kotlin backend. Basically, I have no good solution for injecting stuff other than using global variables. I feel dirty doing that.
Component classes is what I kind of need I guess. I will look at that. Thanks @Christian Hausknecht
the problem with declative is that only part of the system is like that. Basically stores, api clients, daos, etc. all need to be wired together. And you need stores in components to access what is in them. So I have a store that uses a service that uses an api client. Then that store has a handler that calls the service that calls our api and puts the response in yet another store. Dependency injection is the way to keep that nicely structured. Koin is the way to do that in Kotlin. Everything else is basically clumsy.
c
So I have a store that uses a service that uses an api client. Then that store has a handler that calls the service that calls our api and puts the response in yet another store.
This is the typical use case for DI as you said and I totally agree. I only have doubts, how to widen this up to also the pure UI declarations! But if you arrive to some basic example please let us know! I am curious to see, how this looks and feels :-)
j
For the record, I gave implementing
Component
a try.
Copy code
class ExplorerSearchBar(
    val exploreQueryContextStore: ExploreQueryContextStore,
    val groupDropDownOptionsStore: GroupDropDownOptionsStore,
    val selectedGroupStore: SelectedGroupStore,
    val selectedObjectTypeStore: SelectedObjectTypeStore,
    val searchQueryInputFieldStore: SearchQueryInputFieldStore
) : Component<Div> {
    override fun render(
        context: RenderContext,
        styling: BoxParams.() -> Unit,
        baseClass: StyleClass,
        id: String?,
        prefix: String
    ) = context.flexBox({
        margin { smaller }
        direction { row }
        justifyContent { spaceAround }
    }) {
        box({ width { full } }) {
            this@ExplorerSearchBar.groupDropDownOptionsStore.data.render { opts ->
                selectField(value = this@ExplorerSearchBar.selectedGroupStore, items = opts) {
                    label { it.name }
                    placeholder("Select a group")
                }
            }
        }
        box({ width { full } }) {
            clickButton {
                text("reload groups")
                icon {
                    fromTheme {
                        refresh
                    }
                }
            } handledBy this@ExplorerSearchBar.groupDropDownOptionsStore.reloadHandler
        }
        selectField(value = this@ExplorerSearchBar.selectedObjectTypeStore, items = ObjectType.values().toList() + null) {
            label { it?.name ?: "all" }
            selectedItem(null)
        }

        box({ width { full } }) {
            inputField(value = this@ExplorerSearchBar.searchQueryInputFieldStore) {
                placeholder("search for objects matching or leave blank")
            }
        }
    }
}
Not sure if I like this. Without the verbosity of this@ExplorerSearchBar it would be alright. It's the downside of Kotlin DSLs; it gets confusing to figure out what is what if you have multiple levels of this implicitly changing to something else. Another issue here is of course that rendering this needs an explicit invocation. I added an extension function to make that less tedious:
Copy code
inline fun <reified T> Component<T>.show(
    context: RenderContext,
    noinline styling: BasicParams.() -> Unit = {},
    baseClass: StyleClass = StyleClass.None,
    id: String? = null,
    prefix: String = T::class.simpleName!!.toLowerCase(),
) = render(context,styling, baseClass,id,prefix)
👍 1
I'm sticking with explicit koin lookups for now. That seems to suck the least.
c
Another issue here is of course that rendering this needs an explicit invocation. I added an extension function to make that less tedious:
Not sure if I understand you correctly: I think every UI framework will need some "entry point" in order to start the actual rendering process. Calling a method is not that exotic imho and a lot better than abusing a ctor call for that. Other frameworks might hide this explicit invocation by some kind of special wrapping object, that does the call inside, but yes, someone has to invoke the top level rendering method anyway. We have not added such a ``show`` method per default, as we rely on factory functions for each component, that behave more or less exactly like your interface extension.
I'm sticking with explicit koin lookups for now. That seems to suck the least.
Can you provide some example of how you compose everything?
j
@Christian Hausknecht the show function simply calls render with some default values for most of its arguments. It would be nice if render itself had these arguments. That way I can just call myComponent.show(this) in a render context instead of having to provide five arguments. Most of the render implementations I've seen use companion functions that then call render with default arguments. I guess we can do that too. Basically, we have koin initialize stores, services, api clients, etc. Some 40 or so things altogether. We have simple component extension functions that access a global variable with the koin context to look up the things they need. This is what I would normally constructor inject. 2-5 dependencies per component typically. We have dozens of different components. So that's a lot of dependency micro managing. Passing this stuff via arguments seems worse.
c
We have simple component extension functions that access a global variable with the koin context to look up the things they need.
And this is the [Service Locator Anti-Pattern](https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) as far as I understand. The main problem is how to combine the declarative world with the OOP approach when it comes to initialization. This seems to be a hard problem in a way, as imho the UI declaration is the object construction. So you would try to use an injector framework inside of the injecting configuration. I am still not sure, whether and how this could be done in a good way. Moving around a mighty god object to look up everything introduces imho more complexity without adding enough benefit. Right now I can only imagine to set up the infrastructure objects by using classical DI approach (independent of or whether to apply a framework for that) and then bind those to some vals that are kind of "global" for the UI declaration scope. Not 100% accurate, but the only solution I could imagine. It might be possible to apply the redux architecture for a fritz2 app in order to manage the state with a central object. All services and other infrastructure objects could then initially be setup by DI in a single kind of store object. Only this has then to present within the UI declaration. Not sure how to realize the derivation of fitting ``Store`` implementations as adapters for the fritz2 components then...