Ilya Tel
01/23/2023, 8:15 AMfilterPanel
filterAutocompleteItem
houseAutocomplete
And I want to use them like this:
filterPanel(submitFormCallback = {
println(it.getData())
}) {
filterAutocompleteItem("Address") {
houseAutocomplete()
}
}
My problem is that I can't set the value with Any type.
To make it clearer what I mean and why I need this, here is my case:
In houseAutocomplete()
, I receive data from the backend and form a list of options, when the user clicks, I want to set my data class
as the field value for the form
.
But I'm limited by the fact that the field type I'm using on the form only accepts a string as a value (which is logical and correct).
Also, I can't use Serialization for the value, since I don't want to show the user the complete serialized object.
What I want is the ability to set a custom value for a form field. It does not need to be serialized or deserialized. Since it does not need to be explicitly shown to the user.
For example, houseAutocomplete()
could set
data class AutocompleteOption(val label: String, val value: Any)
as the customValue
for textInput
and just a label: String
as the value
And for the form one could use the getCustomData()
method to get the custom values
What do you think about it ? I understood correctly that this cannot be achieved now ? Or is there another way ?Robert Jaros
01/23/2023, 8:46 AMFilterPanel
a FormPanel<T>
? What is T
in your case? How do you use form controls (e.g. TextInput
)? How do you bind()
your controls to the form panel?Ilya Tel
01/23/2023, 12:04 PMdata class FormState(val fields: ObservableValue<MutableMap<String, Any?>> = ObservableValue(mutableMapOf())) {
fun clear() { fields.setState(fields.value.also { it.clear() }) }
fun get(key: String) = fields.value[key]
fun<T> getSub(key: String): ObservableState<T> = fields.sub { it[key] as T }
fun set(pair: Pair<String, Any?>) { fields.setState(fields.value.also { it[pair.first] = pair.second })}
}
Here's my FilterPanel
fun Container.filterPanel(submitFormCallback: ((FormState) -> Unit), contentBuilder: FormPanel<Map<String, Any?>>.(FormState) -> Unit) {
val state = FormState()
div (className = "filter__list"){
form (className = "filter__blocks -collapsed") {
contentBuilder(state)
div(className = "filter__block important") {
button("", className = "btn-apply").onClick { submitFormCallback(state) }
div {
button("", className = "btn-reset").onClick { state.clear(); submitFormCallback(state) }
button("", className = "btn-all")
}
}
}
}
}
fun FormPanel<Map<String, Any?>>.filterField(label: String, state: FormState, contentBuilder: Container.(FormState) -> Unit) {
div (className = "filter__block important") {
div(className = "field") {
p(label)
contentBuilder(state)
}
}
}
fun FormPanel<Map<String, Any?>>.houseAutocomplete(state: FormState, key: String = "house_id") {
textInput().bind(ObservableValue("Some Start Value")) { stateValue ->
startValue = stateValue
onInput {
state.set(key to AutocompleteOption(value, 5))
}
}
}
and i use it like this
filterPanel(submitFormCallback = {
println(it.fields.value)
}) { formState ->
filterField("Address") { houseAutocomplete(formState) }
}
Everything seems to work (at least setting the values), but when I hit the clear button, the state is reset but the component doesn't re-render, which again makes sense because it's bound to ObservableValue("Some Start Value").
And now the problem is that I do not know what I need to bind the component to so that its value is cleared.
Maybe I've overcomplicated it.
Initially, I wanted to be able to save a custom value for the form field.Ilya Tel
01/23/2023, 12:41 PMdata class FormValue(
val value: Any? = null,
val customValue: Any? = null
)
data class FormState(val fields: ObservableValue<MutableMap<String, FormValue>> = ObservableValue(mutableMapOf())) {
fun clear() { fields.setState(fields.value.also { it.clear() }) }
fun get(key: String) = fields.value[key]
fun <T>getSub(key: String): ObservableState<T> = fields.sub { it[key]?.value as T }
fun set(pair: Pair<String, FormValue>) { fields.setState(fields.value.also { it[pair.first] = pair.second })}
}
Robert Jaros
01/23/2023, 1:19 PMdata class CustomValue(val data: Any?)
data class FormValue(
val value: String? = null,
val customValue: Any? = null
)
data class FormState(val fields: ObservableValue<Map<String, FormValue>> = ObservableValue(emptyMap())) {
fun clear() {
fields.setState(emptyMap())
}
fun get(key: String) = fields.value[key]
fun getSub(key: String): ObservableState<FormValue?> = fields.sub { it[key] }
fun set(pair: Pair<String, FormValue>) {
fields.value += pair
}
}
fun Container.filterPanel(
submitFormCallback: ((FormState) -> Unit),
contentBuilder: FormPanel<Map<String, Any?>>.(FormState) -> Unit
) {
val state = FormState().apply {
set("house_id" to FormValue("My house", CustomValue("My house")))
}
div(className = "filter__list") {
form(className = "filter__blocks -collapsed") {
contentBuilder(state)
div(className = "filter__block important") {
button("apply", className = "btn-apply").onClick {
submitFormCallback(state)
}
div {
button("reset", className = "btn-reset").onClick { state.clear(); submitFormCallback(state) }
button("", className = "btn-all").onClick {
}
}
}
}
}
}
fun FormPanel<Map<String, Any?>>.filterField(
label: String,
state: FormState,
contentBuilder: Container.(FormState) -> Unit
) {
div(className = "filter__block important") {
div(className = "field") {
p(label)
contentBuilder(state)
}
}
}
fun FormPanel<Map<String, Any?>>.houseAutocomplete(state: FormState, key: String = "house_id") {
val sub = state.getSub(key)
textInput() {
bind(sub, { it?.value }) { value ->
this.value = value
}
onInput {
state.set(key to FormValue(value, sub.getState()?.customValue))
}
}
}
Robert Jaros
01/23/2023, 1:21 PMRobert Jaros
01/23/2023, 1:23 PMIlya Tel
01/23/2023, 4:30 PMonInput {
inside the bind
function lambda
textInput().bind(subscribe) {
onInput {//it was a bad idea
}
}
I also took the rest of your recommendations and will do as you suggested.