From bd5ca4eb07b8b7f6cff1ac8abfb34cfb638076e4 Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Tue, 5 Nov 2019 10:48:33 +0100 Subject: Fix SelectRemote initialization. Add new option to preload values. --- .../treksoft/kvision/form/select/SelectRemote.kt | 9 +- .../kvision/form/select/SelectRemoteInput.kt | 165 ++++++++++++++------- 2 files changed, 119 insertions(+), 55 deletions(-) (limited to 'kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision') diff --git a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt index a81d7f2d..4d90d18b 100644 --- a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt +++ b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemote.kt @@ -44,6 +44,7 @@ import pl.treksoft.kvision.utils.SnOn * @param name the name attribute of the generated HTML input element * @param multiple allows multiple value selection (multiple values are comma delimited) * @param ajaxOptions additional options for remote data source + * @param preload preload all options from remote data source * @param label label text bound to the input element * @param rich determines if [label] can contain HTML code */ @@ -56,6 +57,7 @@ open class SelectRemote( name: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, + preload: Boolean = false, label: String? = null, rich: Boolean = false ) : SimplePanel(setOf("form-group")), StringFormControl { @@ -158,7 +160,7 @@ open class SelectRemote( private val idc = "kv_form_SelectRemote_$counter" final override val input: SelectRemoteInput = SelectRemoteInput( - value, serviceManager, function, stateFunction, multiple, ajaxOptions, + value, serviceManager, function, stateFunction, multiple, ajaxOptions, preload, setOf("form-control") ).apply { this.id = idc @@ -267,8 +269,8 @@ fun Container.selectRemote( value: String? = null, serviceManager: KVServiceManager, function: suspend T.(String?, String?, String?) -> List, stateFunction: (() -> String)? = null, - name: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, label: String? = null, - rich: Boolean = false, init: (SelectRemote.() -> Unit)? = null + name: String? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, preload: Boolean = false, + label: String? = null, rich: Boolean = false, init: (SelectRemote.() -> Unit)? = null ): SelectRemote { val selectRemote = SelectRemote( @@ -279,6 +281,7 @@ fun Container.selectRemote( name, multiple, ajaxOptions, + preload, label, rich ).apply { init?.invoke(this) } diff --git a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt index f1643e3d..ac05a6d3 100644 --- a/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt +++ b/kvision-modules/kvision-bootstrap-select-remote/src/main/kotlin/pl/treksoft/kvision/form/select/SelectRemoteInput.kt @@ -22,7 +22,7 @@ package pl.treksoft.kvision.form.select import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.asDeferred +import kotlinx.coroutines.await import kotlinx.coroutines.launch import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.list @@ -48,6 +48,7 @@ external fun decodeURIComponent(encodedURI: String): String * @param stateFunction a function to generate the state object passed with the remote request * @param multiple allows multiple value selection (multiple values are comma delimited) * @param ajaxOptions additional options for remote data source + * @param preload preload all options from remote data source * @param classes a set of CSS class names */ @UseExperimental(ImplicitReflectionSerializer::class) @@ -55,75 +56,125 @@ open class SelectRemoteInput( value: String? = null, serviceManager: KVServiceManager, function: suspend T.(String?, String?, String?) -> List, - stateFunction: (() -> String)? = null, + private val stateFunction: (() -> String)? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, + private val preload: Boolean = false, classes: Set = setOf() ) : SelectInput(null, value, multiple, null, classes) { + private val url: String + private val labelsCache = mutableMapOf() + private var initRun = false + init { - val (url, method) = + val (_url, method) = serviceManager.getCalls()[function.toString().replace("\\s".toRegex(), "")] ?: throw IllegalStateException("Function not specified!") - val data = obj { - q = "{{{q}}}" - } - val tempAjaxOptions = ajaxOptions ?: AjaxOptions() - this.ajaxOptions = tempAjaxOptions.copy( - url = url, - preprocessData = { - @Suppress("UnsafeCastFromDynamic") - JSON.plain.parse(RemoteOption.serializer().list, it.result as String).map { - obj { - this.value = it.value - if (it.text != null) this.text = it.text - if (it.className != null) this.`class` = it.className - if (it.disabled) this.disabled = true - if (it.divider) this.divider = true - this.data = obj { - if (it.subtext != null) this.subtext = it.subtext - if (it.icon != null) this.icon = it.icon - if (it.content != null) this.content = it.content + this.url = _url + if (!preload) { + val data = obj { + q = "{{{q}}}" + } + val tempAjaxOptions = ajaxOptions ?: AjaxOptions() + this.ajaxOptions = tempAjaxOptions.copy( + url = url, + preprocessData = { + @Suppress("UnsafeCastFromDynamic") + JSON.plain.parse(RemoteOption.serializer().list, it.result as String).map { + obj { + this.value = it.value + if (it.text != null) this.text = it.text + if (it.className != null) this.`class` = it.className + if (it.disabled) this.disabled = true + if (it.divider) this.divider = true + this.data = obj { + if (it.subtext != null) this.subtext = it.subtext + if (it.icon != null) this.icon = it.icon + if (it.content != null) this.content = it.content + } } + }.toTypedArray() + }, + data = data, + beforeSend = { _, b -> + @Suppress("UnsafeCastFromDynamic") + val q = decodeURIComponent(b.data.substring(2)) + val state = stateFunction?.invoke() + b.data = JSON.plain.stringify(JsonRpcRequest(0, url, listOf(q, this.value, state))) + true + }, + httpType = HttpType.valueOf(method.name), + cache = false, + preserveSelected = ajaxOptions?.preserveSelected ?: true + ) + if (this.ajaxOptions?.emptyRequest == true) { + this.setInternalEventListener> { + shownBsSelect = { + val input = self.getElementJQuery()?.parent()?.find("input") + input?.trigger("keyup", null) + input?.hide(0) } - }.toTypedArray() - }, - data = data, - beforeSend = { _, b -> - @Suppress("UnsafeCastFromDynamic") - val q = decodeURIComponent(b.data.substring(2)) - val state = stateFunction?.invoke() - b.data = JSON.plain.stringify(JsonRpcRequest(0, url, listOf(q, this.value, state))) - true - }, - httpType = HttpType.valueOf(method.name), - cache = false, - preserveSelected = ajaxOptions?.preserveSelected ?: true - ) - if (value != null) { + } + + } + } else { GlobalScope.launch { val callAgent = CallAgent() val state = stateFunction?.invoke() - val initials = callAgent.remoteCall( + val values = callAgent.remoteCall( url, - JSON.plain.stringify(JsonRpcRequest(0, url, listOf(null, value, state))), + JSON.plain.stringify(JsonRpcRequest(0, url, listOf(null, null, state))), HttpMethod.POST - ).asDeferred().await() - JSON.plain.parse(RemoteOption.serializer().list, initials.result as String).map { - add(SelectOption(it.value, it.text, selected = true)) + ).await() + JSON.plain.parse(RemoteOption.serializer().list, values.result as String).forEach { + add( + SelectOption( + it.value, + it.text, + it.subtext, + it.icon, + it.divider, + it.disabled, + false, + it.className?.let { setOf(it) } ?: setOf())) } - this@SelectRemoteInput.refreshSelectInput() } } - if (this.ajaxOptions?.emptyRequest == true) { - this.setInternalEventListener> { - shownBsSelect = { - val input = self.getElementJQuery()?.parent()?.find("input") - input?.trigger("keyup", null) - input?.hide(0) + } + + @Suppress("UnsafeCastFromDynamic") + override fun refreshState() { + value?.let { + if (multiple) { + getElementJQueryD()?.selectpicker("val", it.split(",").toTypedArray()) + } else { + getElementJQueryD()?.selectpicker("val", it) + } + val elementValue = getElementJQuery()?.`val`() + @Suppress("SimplifyBooleanWithConstants") + if (!preload && elementValue == null && initRun == false) { + initRun = true + GlobalScope.launch { + val labels = labelsCache.getOrPut(it) { + val callAgent = CallAgent() + val state = stateFunction?.invoke() + val initials = callAgent.remoteCall( + url, + JSON.plain.stringify(JsonRpcRequest(0, url, listOf(null, it, state))), + HttpMethod.POST + ).await() + JSON.plain.parse(RemoteOption.serializer().list, initials.result as String).map { + it.text + }.filterNotNull().joinToString(", ") + } + val button = getElementJQuery()?.next() + button?.removeClass("bs-placeholder") + button?.prop("title", labels) + button?.find(".filter-option-inner-inner")?.html(labels) + initRun = false } } - - } + } ?: getElementJQueryD()?.selectpicker("val", null) } } @@ -139,10 +190,20 @@ fun Container.selectRemoteInput( stateFunction: (() -> String)? = null, multiple: Boolean = false, ajaxOptions: AjaxOptions? = null, + preload: Boolean = false, classes: Set = setOf(), init: (SelectRemoteInput.() -> Unit)? = null ): SelectRemoteInput { val selectRemoteInput = - SelectRemoteInput(value, serviceManager, function, stateFunction, multiple, ajaxOptions, classes).apply { + SelectRemoteInput( + value, + serviceManager, + function, + stateFunction, + multiple, + ajaxOptions, + preload, + classes + ).apply { init?.invoke(this) } this.add(selectRemoteInput) -- cgit