DatabaseControl

class DatabaseControl(label: String?, val query: QueryFragment, val displayColumn: String, val idColumn: String = "id", val orderBy: String = displayColumn, val searchFilter: (String) -> QueryFragment?? = null, val pageSize: Long = 10, required: Boolean? = false, dependencies: Map<String, ControlDependency<*>>? = null, actions: List<ControlAction<Int>>? = null) : AsyncPaginatedDropdownControl<Int> , KoinComponent

A control for choosing a database record from a dropdown list.

The source is any query. The control wraps it in a subquery and adds searching, sorting and paging, never rewriting it - the same contract as buildQuery() in report-engine. Joins, a fixed filter or a computed item text therefore go into the SQL, not into the control's parameters:

"author_id" to DatabaseControl(
label = BooksTr.Form.author(),
query = QueryFragment("SELECT id, name FROM books.authors"),
displayColumn = "name"
)

The control's value is the idColumn of the selected row. The whole row - including the columns the control does not show - goes to ActionContext.payload on selection, so an action can fill other controls from it.

Parameters

query

Source query. Its parameters are attached to every call.

displayColumn

Result column shown as the item text.

idColumn

Result column that is the control's value.

orderBy

Sorting of the list, by displayColumn by default. idColumn is always appended at the end as a tie-breaker: without it paging with OFFSET is not deterministic, and when further pages are appended an item with a repeated text at a page boundary could show up twice or not at all.

searchFilter

Search condition for the typed text. No lambda, or null from it, means the default displayColumn::text ILIKE %text%. The same shape as buildQuickSearch in report-engine.

pageSize

Number of items loaded at once.

Constructors

Link copied to clipboard
constructor(label: String?, query: QueryFragment, displayColumn: String, idColumn: String = "id", orderBy: String = displayColumn, searchFilter: (String) -> QueryFragment?? = null, pageSize: Long = 10, required: Boolean? = false, dependencies: Map<String, ControlDependency<*>>? = null, actions: List<ControlAction<Int>>? = null)

Properties

Link copied to clipboard
private val actions: List<ControlAction<T>>?
Link copied to clipboard
private val db: OctaviusClient
Link copied to clipboard
protected open val debounceMs: Long = 0

How long to wait after the last typed character before the query goes out.

Link copied to clipboard
Link copied to clipboard
private val displayColumn: String
Link copied to clipboard
protected lateinit var errorManager: ErrorManager
Link copied to clipboard
protected lateinit var formActionTrigger: FormActionTrigger
Link copied to clipboard
protected lateinit var formSchema: FormSchema
Link copied to clipboard
protected lateinit var formState: FormState
Link copied to clipboard
protected val hasStandardLayout: Boolean
Link copied to clipboard
private val idColumn: String
Link copied to clipboard
internal val label: String?
Link copied to clipboard
private val orderBy: String
Link copied to clipboard
private val pageSize: Long
Link copied to clipboard
private val query: QueryFragment
Link copied to clipboard
internal val required: Boolean?
Link copied to clipboard
private val searchFilter: (String) -> QueryFragment??
Link copied to clipboard
Link copied to clipboard
protected open val validator: ControlValidator<Int>

Validator responsible for validating this control. Every control type provides its own implementation (e.g. StringValidator). Empty implementation by default

Functions

Link copied to clipboard
protected open override fun acceptsValue(value: Any): Boolean

Whether this control can hold value, i.e. whether it is a value of type T (and for a choice from fixed options - one of them). Checked for initial values and in updateControl. Controls without a value of their own accept nothing: a section, a button, and a repeatable list, which manages its rows itself. Containers check the shape of their initial data in setInitValue.

Link copied to clipboard
private fun buildSearchFilter(searchQuery: String): QueryFragment
Link copied to clipboard
internal open fun childContexts(controlContext: ControlContext): List<Pair<ControlContext, Control<*>>>

Children of this control in the current form state, each with a context in which this control is the parent: a section has one set, a repeatable - one per row. The engine walks down the tree through them when initializing, validating and collecting data.

Link copied to clipboard
protected open fun convertToResult(controlContext: ControlContext, state: ControlState<*>): ControlResultData

Converts the control's internal state (ControlState) into a result to save (ControlResultData). Can be overridden for custom conversions (e.g. in RepeatableControl).

Link copied to clipboard
internal open fun copyInitToValue(value: Int): Int

Makes a deep copy of the initial value for the current value. Matters for mutable types (e.g. List), so that changes to value do not affect initValue.

Link copied to clipboard
@Composable
protected open override fun Display(controlContext: ControlContext, controlState: ControlState<Int>, isRequired: Boolean)

Renders the control's own UI. Every concrete control type (e.g. StringControl) implements it.

Link copied to clipboard
@Composable
protected fun DisplayFieldErrors(controlContext: ControlContext)

Renders this control's validation errors.

Link copied to clipboard
protected fun executeActions(controlContext: ControlContext, newValue: Int?, scope: CoroutineScope, payload: Any? = null)

Runs the actions defined for this control, usually after a value change.

Link copied to clipboard
internal fun executeInitActions(controlContext: ControlContext, currentValue: Int?, scope: CoroutineScope)
Link copied to clipboard
open fun getKoin(): Koin
Link copied to clipboard
internal fun getResult(controlContext: ControlContext, state: ControlState<*>): ControlResultData

Returns the control's result (current and initial value) for further processing. If the control is invisible, its currentValue is set to null.

Link copied to clipboard
internal open fun initializeControlLifecycle(formState: FormState, formSchema: FormSchema, errorManager: ErrorManager, formActionTrigger: FormActionTrigger)

Sets the references to the form components. This is the second stage of a control's initialization, called by FormHandler. Gives access to the global state, the schema and the error manager. Also lets particular controls run additional initialization steps they need

Link copied to clipboard
@Composable
private fun LoadingIndicator()

Renders the loading indicator.

Link copied to clipboard
protected open suspend override fun loadPage(searchQuery: String, page: Long): DropdownPage<Int>

Subclasses implement this to provide the data of a given page. It is suspend because the loading is asynchronous.

Link copied to clipboard
@Composable
private fun MenuContent(options: List<DropdownOption<T>>, isRequired: Boolean, isLoadingFirstPage: Boolean, isLoadingMore: Boolean, errorMessage: String?, onOptionSelected: (DropdownOption<T>?) -> Unit)

Renders the main content of the menu.

Link copied to clipboard
protected fun mismatch(value: Any): String

Describes value as not fitting this control, for exception messages.

Link copied to clipboard
@Composable
private fun OptionsList(options: List<DropdownOption<T>>, isRequired: Boolean, onOptionSelected: (DropdownOption<T>?) -> Unit)

Renders the list of options to choose from.

Link copied to clipboard
internal open fun registerChildrenInGlobalMap(controlContext: ControlContext): Map<String, Control<*>>

Registers the children in the global map Holds references to the controls defined inside a control that has its own

Link copied to clipboard
internal fun rejectionOf(value: Any): String?

Why the control does not accept value, or null when it does.

Link copied to clipboard
@Composable
internal fun Render(controlContext: ControlContext, controlState: ControlState<*>)

Renders the control in the user interface. This is the entry point for FormView. Handles visibility.

Link copied to clipboard
@Composable
protected open override fun ColumnScope.RenderMenuItems(controlContext: ControlContext, scope: CoroutineScope, controlState: ControlState<Int>, menuScrollState: ScrollState, closeMenu: () -> Unit)

Subclasses implement this to render the content of the dropdown menu.

Link copied to clipboard
protected open suspend override fun resolveDisplayText(value: Int): String?

Called once per value, outside rendering, and the result is stored in ControlState.displayText.

Link copied to clipboard
@Composable
private fun SearchField(searchQuery: String, onQueryChange: (String) -> Unit)

Renders the search field inside the menu.

Link copied to clipboard
internal open fun setInitValue(controlContext: ControlContext, value: Any?): ControlState<Int>

Sets the control's initial value and creates its state (ControlState).

Link copied to clipboard
internal fun validateControl(controlContext: ControlContext, state: ControlState<*>)

Runs this control's validation with its validator.