From 6066afa3b9c950021253e74e966b1763c2883163 Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Sat, 1 Feb 2020 02:03:46 +0100 Subject: Add support for custom type fields in Form/FormPanel model class. --- src/main/kotlin/pl/treksoft/kvision/form/Form.kt | 51 ++++++++++++++++++---- .../kotlin/pl/treksoft/kvision/form/FormControl.kt | 2 +- .../kotlin/pl/treksoft/kvision/form/FormPanel.kt | 41 +++++++++++++++-- 3 files changed, 81 insertions(+), 13 deletions(-) (limited to 'src/main') 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(private val map: Map) : Map(private val panel: FormPanel? = null, private val serializer: KSerializer) { +class Form( + private val panel: FormPanel? = null, + private val serializer: KSerializer, + private val customSerializers: Map, KSerializer<*>>? = null +) { val modelFactory: (Map) -> K val fields: MutableMap = mutableMapOf() @@ -106,7 +110,12 @@ class Form(private val panel: FormPanel? = 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) ) @@ -141,6 +150,24 @@ class Form(private val panel: FormPanel? = null, private val seriali return addInternal(key, control, required, requiredMessage, validatorMessage, validator) } + /** + * 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 addCustom( + key: KProperty1, control: C, required: Boolean = false, requiredMessage: String? = null, + validatorMessage: ((C) -> String?)? = null, + validator: ((C) -> Boolean?)? = null + ): Form { + return addInternal(key, control, required, requiredMessage, validatorMessage, validator) + } + /** * Adds a boolean control to the form. * @param key key identifier of the control @@ -278,13 +305,22 @@ class Form(private val panel: FormPanel? = 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(private val panel: FormPanel? = null, private val seriali @UseExperimental(ImplicitReflectionSerializer::class) inline fun create( panel: FormPanel? = null, + customSerializers: Map, KSerializer<*>>? = null, noinline init: (Form.() -> Unit)? = null ): Form { - 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( method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null, private val type: FormType? = null, condensed: Boolean = false, horizRatio: FormHorizontalRatio = FormHorizontalRatio.RATIO_2, classes: Set = setOf(), - serializer: KSerializer + serializer: KSerializer, customSerializers: Map, KSerializer<*>>? = null ) : SimplePanel(classes) { /** @@ -176,7 +177,7 @@ open class FormPanel( * Internal property. */ @Suppress("LeakingThis") - val form = Form(this, serializer) + val form = Form(this, serializer, customSerializers) /** * @suppress * Internal property. @@ -278,6 +279,26 @@ open class FormPanel( return addInternal(key, control, required, requiredMessage, legend, validatorMessage, validator) } + /** + * 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 addCustom( + key: KProperty1, control: C, required: Boolean = false, requiredMessage: String? = null, + legend: String? = null, + validatorMessage: ((C) -> String?)? = null, + validator: ((C) -> Boolean?)? = null + ): FormPanel { + return addInternal(key, control, required, requiredMessage, legend, validatorMessage, validator) + } + /** * Adds a boolean control to the form panel. * @param key key identifier of the control @@ -442,10 +463,21 @@ open class FormPanel( method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null, type: FormType? = null, condensed: Boolean = false, horizRatio: FormHorizontalRatio = FormHorizontalRatio.RATIO_2, classes: Set = setOf(), + customSerializers: Map, KSerializer<*>>? = null, noinline init: (FormPanel.() -> Unit)? = null ): FormPanel { 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 Container.formPanel( method: FormMethod? = null, action: String? = null, enctype: FormEnctype? = null, type: FormType? = null, condensed: Boolean = false, horizRatio: FormHorizontalRatio = FormHorizontalRatio.RATIO_2, classes: Set = setOf(), + customSerializers: Map, KSerializer<*>>? = null, noinline init: (FormPanel.() -> Unit)? = null ): FormPanel { - val formPanel = create(method, action, enctype, type, condensed, horizRatio, classes) + val formPanel = create(method, action, enctype, type, condensed, horizRatio, classes, customSerializers) init?.invoke(formPanel) this.add(formPanel) return formPanel -- cgit