aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Jaros <rjaros@finn.pl>2020-02-01 02:03:46 +0100
committerRobert Jaros <rjaros@finn.pl>2020-02-01 02:03:46 +0100
commit6066afa3b9c950021253e74e966b1763c2883163 (patch)
tree3dbcba3499014e5643fefc1b53202b44827bce26
parent268b6f28ccdd8ea247496b0ac889aec05badd9b6 (diff)
downloadkvision-6066afa3b9c950021253e74e966b1763c2883163.tar.gz
kvision-6066afa3b9c950021253e74e966b1763c2883163.tar.bz2
kvision-6066afa3b9c950021253e74e966b1763c2883163.zip
Add support for custom type fields in Form/FormPanel model class.
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/Form.kt51
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt2
-rw-r--r--src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt41
3 files changed, 81 insertions, 13 deletions
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/Form.kt b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt
index 8f63662e..1c17999d 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/Form.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/Form.kt
@@ -30,9 +30,9 @@ import pl.treksoft.kvision.i18n.I18n.trans
import pl.treksoft.kvision.types.DateSerializer
import pl.treksoft.kvision.types.KFile
import pl.treksoft.kvision.types.toStringF
-import pl.treksoft.kvision.utils.JSON
import kotlin.js.Date
import kotlin.js.Json
+import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
/**
@@ -75,7 +75,11 @@ private class FormMapWrapper<out V>(private val map: Map<String, V>) : Map<Strin
* @param serializer a serializer for model type
*/
@Suppress("TooManyFunctions")
-class Form<K : Any>(private val panel: FormPanel<K>? = null, private val serializer: KSerializer<K>) {
+class Form<K : Any>(
+ private val panel: FormPanel<K>? = null,
+ private val serializer: KSerializer<K>,
+ private val customSerializers: Map<KClass<*>, KSerializer<*>>? = null
+) {
val modelFactory: (Map<String, Any?>) -> K
val fields: MutableMap<String, FormControl> = mutableMapOf()
@@ -106,7 +110,12 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali
else -> listOf(entry.key to entry.value)
}
}.toMap()
- Mapper(context = serializersModuleOf(Date::class, DateSerializer)).unmapNullable(
+ val serializersModule = if (customSerializers == null) {
+ serializersModuleOf(Date::class, DateSerializer)
+ } else {
+ serializersModuleOf(customSerializers + (Date::class to DateSerializer))
+ }
+ Mapper(context = serializersModule).unmapNullable(
serializer,
FormMapWrapper(map)
)
@@ -142,6 +151,24 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali
}
/**
+ * Adds a string control to the form bound to custom field type.
+ * @param key key identifier of the control
+ * @param control the string form control
+ * @param required determines if the control is required
+ * @param requiredMessage optional required validation message
+ * @param validatorMessage optional function returning validation message
+ * @param validator optional validation function
+ * @return current form
+ */
+ fun <C : StringFormControl> addCustom(
+ key: KProperty1<K, Any?>, control: C, required: Boolean = false, requiredMessage: String? = null,
+ validatorMessage: ((C) -> String?)? = null,
+ validator: ((C) -> Boolean?)? = null
+ ): Form<K> {
+ return addInternal(key, control, required, requiredMessage, validatorMessage, validator)
+ }
+
+ /**
* Adds a boolean control to the form.
* @param key key identifier of the control
* @param control the boolean form control
@@ -278,13 +305,22 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali
return modelFactory(map.withDefault { null })
}
-
/**
* Returns current data model as JSON.
* @return data model as JSON
*/
fun getDataJson(): Json {
- return kotlin.js.JSON.parse(JSON.plain.stringify(serializer, getData()))
+ val serializersModule = if (customSerializers == null) {
+ serializersModuleOf(Date::class, DateSerializer)
+ } else {
+ serializersModuleOf(customSerializers + (Date::class to DateSerializer))
+ }
+ return JSON.parse(
+ kotlinx.serialization.json.Json(context = serializersModule).stringify(
+ serializer,
+ getData()
+ )
+ )
}
/**
@@ -325,14 +361,13 @@ class Form<K : Any>(private val panel: FormPanel<K>? = null, private val seriali
@UseExperimental(ImplicitReflectionSerializer::class)
inline fun <reified K : Any> create(
panel: FormPanel<K>? = null,
+ customSerializers: Map<KClass<*>, KSerializer<*>>? = null,
noinline init: (Form<K>.() -> Unit)? = null
): Form<K> {
- val form = Form(panel, K::class.serializer())
+ val form = Form(panel, K::class.serializer(), customSerializers)
init?.invoke(form)
return form
}
-
-
}
}
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt
index 9c9aba80..1e915ac9 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/FormControl.kt
@@ -213,7 +213,7 @@ interface StringFormControl : FormControl {
override fun getValue(): String? = value
override fun setValue(v: Any?) {
- value = v as? String
+ value = v as? String ?: v?.toString()
}
override fun getValueAsString(): String? = value
diff --git a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt
index 62f3395e..77c324c5 100644
--- a/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt
+++ b/src/main/kotlin/pl/treksoft/kvision/form/FormPanel.kt
@@ -35,6 +35,7 @@ import pl.treksoft.kvision.panel.SimplePanel
import pl.treksoft.kvision.types.KFile
import kotlin.js.Date
import kotlin.js.Json
+import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
/**
@@ -106,7 +107,7 @@ open class FormPanel<K : Any>(
method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null,
private val type: FormType? = null, condensed: Boolean = false,
horizRatio: FormHorizontalRatio = FormHorizontalRatio.RATIO_2, classes: Set<String> = setOf(),
- serializer: KSerializer<K>
+ serializer: KSerializer<K>, customSerializers: Map<KClass<*>, KSerializer<*>>? = null
) : SimplePanel(classes) {
/**
@@ -176,7 +177,7 @@ open class FormPanel<K : Any>(
* Internal property.
*/
@Suppress("LeakingThis")
- val form = Form(this, serializer)
+ val form = Form(this, serializer, customSerializers)
/**
* @suppress
* Internal property.
@@ -279,6 +280,26 @@ open class FormPanel<K : Any>(
}
/**
+ * Adds a string control to the form panel bound to custom field type.
+ * @param key key identifier of the control
+ * @param control the string form control
+ * @param required determines if the control is required
+ * @param requiredMessage optional required validation message
+ * @param legend put this control inside a fieldset with given legend
+ * @param validatorMessage optional function returning validation message
+ * @param validator optional validation function
+ * @return current form panel
+ */
+ open fun <C : StringFormControl> addCustom(
+ key: KProperty1<K, Any?>, control: C, required: Boolean = false, requiredMessage: String? = null,
+ legend: String? = null,
+ validatorMessage: ((C) -> String?)? = null,
+ validator: ((C) -> Boolean?)? = null
+ ): FormPanel<K> {
+ return addInternal(key, control, required, requiredMessage, legend, validatorMessage, validator)
+ }
+
+ /**
* Adds a boolean control to the form panel.
* @param key key identifier of the control
* @param control the boolean form control
@@ -442,10 +463,21 @@ open class FormPanel<K : Any>(
method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null,
type: FormType? = null, condensed: Boolean = false,
horizRatio: FormHorizontalRatio = FormHorizontalRatio.RATIO_2, classes: Set<String> = setOf(),
+ customSerializers: Map<KClass<*>, KSerializer<*>>? = null,
noinline init: (FormPanel<K>.() -> Unit)? = null
): FormPanel<K> {
val formPanel =
- FormPanel(method, action, enctype, type, condensed, horizRatio, classes, K::class.serializer())
+ FormPanel(
+ method,
+ action,
+ enctype,
+ type,
+ condensed,
+ horizRatio,
+ classes,
+ K::class.serializer(),
+ customSerializers
+ )
init?.invoke(formPanel)
return formPanel
}
@@ -462,9 +494,10 @@ inline fun <reified K : Any> Container.formPanel(
method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null,
type: FormType? = null, condensed: Boolean = false,
horizRatio: FormHorizontalRatio = FormHorizontalRatio.RATIO_2, classes: Set<String> = setOf(),
+ customSerializers: Map<KClass<*>, KSerializer<*>>? = null,
noinline init: (FormPanel<K>.() -> Unit)? = null
): FormPanel<K> {
- val formPanel = create<K>(method, action, enctype, type, condensed, horizRatio, classes)
+ val formPanel = create<K>(method, action, enctype, type, condensed, horizRatio, classes, customSerializers)
init?.invoke(formPanel)
this.add(formPanel)
return formPanel