Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 22, 2021 04:35 pm GMT

Doodle Forms

Doodle forms make data collection simple, while still preserving flexibility to build just the right experience. They hide a lot of the complexity associated with mapping visual components to fields, state management, and validation. The result is an intuitive metaphor modeled around the idea of a constructor.

Doodle also has a set of helpful forms controls that cover a reasonable range of data-types. These make its easy to create forms without much hassle. But there are bound to be cases where more customization is needed. This is why Doodle forms are also extensible, allowing you to fully customize the data they bind to and how each fields is visualized.

Like Constructors

Forms are very similar to constructors in that they have typed parameter lists (fields), and can only "create" instances when all their inputs are valid. Like any constructor, a Form can have optional fields, default values, and arbitrary types for its fields.

While Forms behave like constructors in most ways, they do not actually create instances (only sub-forms do). This means they are not typed. Instead, they take fields and output a corresponding lists of strongly-typed data when all their fields are valid. This notification is intentionally general to allow forms to be used in a wide range of used cases.

Creation

Forms are created using the Form builder function. This function ensures strong typing for fields and the form's "output".

The Form returned from the builder does not expose anything about the data it produces. So all consumption logic goes in the builder block.

val form = Form { this(    field1,    field2,    // ...    onInvalid = {        // called whenever any fields is updated with invalid data    }) { field1, field2, /*...*/ ->        // called each time all fields are updated with valid data    }}

Fields


Each field defined in the Form will be bounded to a single View. These views are defined during field binding using a FieldVisualizer. A visualizer is responsible for taking a Field and its initial state and returning a View. The visualizer then acts as the bridge between the field's state and the View, mapping changes made in the View to the field (this includes validating that input).

Field State

Fields store their data as FieldState. This is a strongly-typed value that can be Valid or Invalid. Valid state contains a value, while invalid state does not. A Form with any invalid fields is invalid itself, and will indicate this by calling onInvalid.

Creating Fields

Fields are created implicitly when FieldVisualizers are bound to a Form. These visualizers can be created using the field builder function, by implementing the interface, or by one of the existing form controls.

Using the builder DSL

import io.nacular.doodle.controls.form.fieldfield<T> {    initial // initial state of the field    state   // mutable state of the field    view {} // view to display for the field}

Implementing interface

import io.nacular.doodle.controls.form.FieldInfoimport io.nacular.doodle.controls.form.FieldVisualizerclass MyVisualizer<T>: FieldVisualizer<T> {    override fun invoke(fieldInfo: FieldInfo<T>): View {        fieldInfo.initial // initial state of the field        fieldInfo.state   // mutable state of the field        return view {}    // view to display for the field    }}

Field Binding


Fields all have an optional initial value. Therefore, each field can be bounded either with a value or without one. The result is 2 different ways of adding a field to a Form.

The following shows how to bind fields that has no default value.

import io.nacular.doodle.controls.form.Formimport io.nacular.doodle.controls.form.textFieldimport io.nacular.doodle.utils.ToStringIntEncoderdata class Person(val name: String, val age: Int)val form = Form { this(    + textField(),    + textField(encoder = ToStringIntEncoder),    + field<Person> { view {} },    // ...    onInvalid = {}) { text: String, number: Int, person: Person ->        // ...    }}

This shows how to bind using initial values.

import io.nacular.doodle.controls.form.Formimport io.nacular.doodle.controls.form.textFieldimport io.nacular.doodle.utils.ToStringIntEncoderdata class Person(val name: String, val age: Int)val form = Form { this(    "Hello"            to textField(),    4                  to textField(encoder = ToStringIntEncoder),    Person("Jack", 55) to field { view {} },    // ...    onInvalid = {}) { text: String, number: Int, person: Person ->        // ...    }}

These examples bind fields that have no names. Doodle has a labeled form control that wraps a control and assigns a name to it.

Note that a visualizer may set a field's state to some valid value at initialization time. This will give the same effect as that field having had a initial value specified that the visualizer accepted.

Forms as Fields

Forms can also have nested forms within them. This is helpful when the field has complex data that can be presented to the user as a set of components. Such cases can be handled with custom visualizers, but many work well using a nested form.

Nested forms are created using the form builder function. It works just like the top-level Form builder, but it actually creates an instance and has access to the initial value it is bound to (if any).

import io.nacular.doodle.controls.form.formimport io.nacular.doodle.controls.form.Formimport io.nacular.doodle.controls.form.textFieldimport io.nacular.doodle.utils.ToStringIntEncoderdata class Person(val name: String, val age: Int)val form = Form { this(       + labeled("Text"  ) { textField() },       + labeled("Number") { textField(encoder = ToStringIntEncoder) },       Person("Jack", 55) to form { this(           initial.map { it.name } to labeled("Name") { textField() },           initial.map { it.age  } to labeled("Age" ) { textField(encoder = ToStringIntEncoder) },           onInvalid = {}       ) { name, age ->           Person(name, age) // construct person when valid       } },       // ...       onInvalid = {}) { text: String, number: Int, person: Person ->       // called each time all fields are updated with valid data   }}

Nested forms can be used with or without initial values like any other field.

Learn more

Doodle is a pure Kotlin UI framework for the Web (and Desktop), that lets you create rich applications without relying on Javascript, HTML or CSS. Check out the documentation and tutorials to learn more.


Original Link: https://dev.to/pusolito/doodle-forms-14cj

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To