Consider to accept "pathes" as part of an Id of RootStores
Currently the path property of a RootStore is always the empty String.
This might be wrong in cases, where you want to manage the mapped portion of some larger model-store within a separate local store.
Because of the current behavior, one looses the capabilities of the automatic validation message mapping of the headless components, which relies on the store.path property.
Imho we can simply "pathify" some given Id, by splitting it with substringAfter(".", "") with empty String as missing delimiter value. This way we bypass API breaking, as long as noone has ever used Ids with "dots" inside (which we should definitely explain and demand in an upcoming revise of the validation section of our documentation). So imho this could be considered "none API breaking" and solve this corner case quite elegantly.
The same must be provided to the inspectorOf factories btw.
So here is some example what one needs to to, if he want to achieve "path-equality" of some external, intermediate store:
// somewhere in the `commonMain` source-set:
@Lenses
data class ExampleItem(
val id: Int = 0,
val content: String
) {
companion object
}
// somewhere in `jsMain`
fun main() {
render {
val item = ExampleItem(42, "foo")
val mainModelStore = storeOf(item, id = "Model")
val storedContent = mainModelStore.map(ExampleItem.content())
val intermediateStore = object : RootStore<String>(
"",
job = job,
// This does not work currently! We get `Hilfsstore..content` as Id and empty string as path!
// But we should be able to gain `id=Hilfsstore.content` and `path=.content` instead!
id = "Hilfsstore.${storedContent.path}"
) {
init {
storedContent.data handledBy update
}
}
/*
* This construct solves the issue: We must `map` the intermediate Store in order to "abuse" the `Lens`
* to repair the path of the new store.
*
* Quite cumbersome!
*/
val mappedIntermediateStore = object : RootStore<String>("", job = job, id = "MappedStore.") {
init {
storedContent.data handledBy update
}
}.map(
lensOf(
storedContent.path.substringAfter(".", ""),
{ it },
{ _, v -> v }
)
)
div("p-8") {
card {
h1("text-2xl") { +"mainModelStore" }
pre("bg-gray-300") {
mainModelStore.data.asString().renderText(into = this)
}
renderDef("Model Id", mainModelStore.id)
renderDef("Path", mainModelStore.path)
renderDef("Content Id", storedContent.id)
renderDef("Path", storedContent.path)
}
card {
h1("text-2xl") { +"intermediateStore" }
pre("bg-gray-300") {
intermediateStore.data.renderText(into = this)
}
renderDef("Hilfsstore Id", intermediateStore.id)
renderDef("Hilfsstore Path", intermediateStore.path)
}
card {
h1("text-2xl") { +"mappedIntermediateStore (current workaround)" }
pre("bg-gray-300") {
mappedIntermediateStore.data.renderText(into = this)
}
renderDef("Hilfsstore Id", mappedIntermediateStore.path)
renderDef("Hilfsstore Path", mappedIntermediateStore.path)
}
}
portalRoot()
}
}
private fun RenderContext.renderDef(term: String, data: String) = dl("flex flex-row gap-4 bg-gray-300") {
dt { +"$term:" }
dd { pre { +data } }
}
Here is the result:
This example clearly shows, that the current behaviour is rather counter intuitive! So we should change the fritz2's current behaviour definitely.