Hello everyone 馃憢 ,
I came up with a working solution for EpoxyModelGroup DSL.
I checked https://github.com/airbnb/epoxy/issues/800 but the solution involved some workaround on building them first and then getting them. Not sure if there are shortcomings to this approach so it would be great to get some 馃憖 on it.
I have a simple EpoxyModelGroup, just a cardview with a linear layout like shown in the grouping models example where I fill that layout up:

My Kotlin class looks like:
class CardViewContainer(vararg models: EpoxyModel<*>) :
EpoxyModelGroup(R.layout.container_card_view, *models)
fun EpoxyController.cardViewContainer(vararg models: EpoxyModel<*>) =
CardViewContainer(*models).addTo(this)
Usage in current version is something like this (non dsl way)
override fun controller() = simpleController(viewModel) { state ->
CardViewEpoxyModelGroup(
TextRowModel_()
.id("text")
.body("Hello")
.style(TextRow.TextStyle.BODY)
// Other models...
).addTo(this)
}
I was looking into the generated EpoxyModelKotlinExtensions.kt and for all the models it automatically adds itself
inline fun EpoxyController.textRow(modelInitializer: TextRowModelBuilder.() -> Unit) {
TextRowModel_().apply { modelInitializer() }.addTo(this)
}
Just to try it out I created manually an equivalent, appending Model at the end and the only difference is that it returns the Model_ instead of adding it
inline fun EpoxyController.textRowModel(modelInitializer: TextRowModelBuilder.() -> Unit): TextRowModel_ =
TextRowModel_().apply { modelInitializer() }
So the new usage becomes something like this:
override fun controller() = simpleController(viewModel) { state ->
cardViewContainer(
textRowModel {
id("text")
body("Hello")
style(TextRow.TextStyle.BODY)
}
)
}
It still requires to have commas between models (so it is not a real DSL?) and you have to remember to use the Model naming at the end of what before was textRow but I think it is an improvement.
Are there any disadvantages to this (apart from having to write the model function manually)?
Would it be a good idea to autogenerate these methods as well?
Any suggestions?
I was testing it with different components, static and also ones where the model changed a lot (text input layout) and I didn't see any issues
The last few questions are:
Could grouping models be used as a replacement for Paris? Having something like a Padding Container for specific cases? (mimicking what flutter does)
From the description of grouping models it says: This is NOT a replacement for creating a custom view but it also mentions add functionality to an existing model via composition... Is this the correct approach if I want to have a simple set of epoxy views (loading, text, buttons, image rows) and let the other epoxy views of the app just be a different EpoxyModelGroups?
Thank you
Hi, I would instead recommend using the new DSL enabled by ModelCollector in https://github.com/airbnb/epoxy/pull/967
you can play with that to see how you can improve the model group creation
The ModelCollector is very helpful. One constraint I noticed is that EpoxyModelGroup requires its items to be provided during instantiation. So if you use the builder generated by EpoxyModelClass/EpoxyAttribute, you have to create a dummy instance to delegate the builder methods into, then copy those attributes into a new instance once we have all the models. Here鈥檚 what I did:
@EpoxyModelClass
abstract class SampleEpoxyModel(
modelList: List<EpoxyModel<*>>
) : EpoxyModelGroup(R.layout.sample, modelList) {
@EpoxyAttribute var size: Int = 0
@EpoxyAttribute var backgroundImageUrl: String? = null
@EpoxyAttribute @ColorInt var backgroundColor: Int? = null
override fun bind(holder: ModelGroupHolder) {
super.bind(holder)
...
}
}
fun ModelCollector.sample(builder: EpoxySampleViewBuilder.() -> Unit): SampleEpoxyModel {
val modelGroup = EpoxySampleViewBuilder().apply { builder() }.build()
add(modelGroup)
return modelGroup
}
class EpoxySampleViewBuilder(
private val sampleEpoxyModel: SampleEpoxyModel_ = SampleEpoxyModel_(listOf(DummyEpoxyModel())) // list with one model required
) : ModelCollector, SampleEpoxyModelBuilder by sampleEpoxyModel {
private val models = mutableListOf<EpoxyModel<*>>()
override fun add(model: EpoxyModel<*>) {
models.add(model)
}
internal fun build() = SampleEpoxyModel_(models)
.size(sampleEpoxyModel.size())
.backgroundImageUrl(sampleEpoxyModel.backgroundImageUrl())
.backgroundColor(sampleEpoxyModel.backgroundColor())
}
Most helpful comment
The ModelCollector is very helpful. One constraint I noticed is that EpoxyModelGroup requires its items to be provided during instantiation. So if you use the builder generated by EpoxyModelClass/EpoxyAttribute, you have to create a dummy instance to delegate the builder methods into, then copy those attributes into a new instance once we have all the models. Here鈥檚 what I did: