form

package module
v0.25.10 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 17, 2025 License: Apache-2.0 Imports: 12 Imported by: 11

README ¶

Form 🤔

GoDoc Version Build Status Go Report Card Codecov

JSON to HTML (Forms for Go)

This Go module generates HTML forms using JSON configurations. It is inspired by JSON-Forms but is not 100% compatible with that standard.

Forms can be defined directly in Go source code, or can be marshaled / unmarshalled from JSON. See the section below for details on JSON marshaling.

Form Definition

Following the design of JSON-Forms, form definitions are split into two parts: the data schema and the UI schema.

Data Schema

Data schemas define the layout of the data that the form will display, relative to object in the application. Schemas is created using the Rosetta schema package. Please see that package for documentation on how to create a Data schema.

UI Schema

UI Schemas define the UI elements that users interact with. In this case, the HTML that will be delivered to the user's web browser.

Example Code
form := New(
	schema.New(schema.Object{
		Properties: schema.ElementMap{
			"name":       schema.String{Required:true},
			"email":      schema.String{Format:"email"},
			"age":        schema.Integer{},
		},
	}),
	Element{
		Type: "test",
		Children: []Element{
			{Type: "text", Path: "name"},
			{Type: "text", Path: "age"},
			{Type: "text", Path: "email"},
		},
	},
)

Form Elements

Each form is defined as a cascading tree of form.Element objects. Each element defines standard properties along with an optional list of child elements

Property Description
type String: Widget type to use when rendering this element. Widgets are defined below
label String: Label to put on the field. Usually renders above input elements in standard text.
description String: Description for the field. Usually renders below input elements in small gray text
path String: Dot notation path to where the field value is stored
id String: DOM ID to use for this field. Useful for some Javascript manipulations
options Object: key/value pairs of non-standard options to pass to the widget element. Options are specific to each element.
children Array of Elements: an array of zero or more elements to render as "child" elements. See "Layouts" below.
readOnly Boolean: If TRUE, then this element is rendered not as an input widget, but as a text value.

Widgets

Form elements are rendered by widgets. This project ships with a standard library of widgets in the widgets package, but it is possible to register custom widgets by passing a widget interface to the form.Use() function.

type: "checkbox"

Documentation TBD

type: "check-button"

This is a custom Hyperscript widget that displays a styled DIV that looks like a selectable button. Underneath is a simple checkbox that can be checked or not. If the checkbox is checked, then the button appears selected. Otherwise, the button appears in a default state.

Option Description
class HTML5 class attribute
script Hyperscript script attribute
type: "check-button-group"

This is a custom Hyperscript widget that uses an Option List to display a group of check-button widgets. Option Lists are defined below, and can be pulled from an option provider or an enum defined in the element options object.

type: "colorpicker"

This is a custom Hyperscript widget that displays an enhanced color picker input. It uses the HTML5 Color picker control in addition to a scripted input element that displays the selected color and its hex code.

This widget does not take any optional arguments.

type: "date"

This renders an HTML5 date element, converting number and string values into a date. This is the preferred method for adding/editing dates using this library

type: datetime"

This renders an HTML5 datetime-local element, converting number and string values into datetimes. This is the preferred method for adding/editing date-times using this library.

type: "heading"

This renders an HTML5 H2 section heading. The element Label is used as the heading. If an element Description is provided, then it is displayed beneath the H2 in a standard div element.

type: "hidden"

This renders an HTML5 hidden input, with one option:

If you want to set a fixed value for this input (instead of pulling its value from the Form's value object) you can use options:{"value":"fixed value here"} to set a fixed value for this hidden element.

type: "html"

This renders custom HTML in the form, using the Description field as the HTML that is entered.

type: "label"

This renders custom content in the form using one or both of the Label and Description fields.

If a Label is provided, then it is entered into the HTML result as plain text. HTML values are escaped, if necessary.

If a Description is provided, then it is entered into the HTML as HTML.

type: "multiselect"

This renders is a custom Hyperscript widget that looks like a scrollable multiselect input.

type: "password"

This renders a HTML5 password input, with the following additional options:

autocomplete HTML5 autocomplete attribute
autofocus HTML5 autofocus attribute
maxlength HTML5 maxlength attribute
minlength HTML5 minlength attribute
pattern HTML5 pattern attribute
placeholder HTML5 placeholder attribute
required HTML5 required attribute
style HTML5 style attribute
type: "radio"

Documentation TBD

type: "select"

Renders an HTML5 select input. The options available in the select box are dependent on the Option List (define below). If the provided lookup code group is "writable" then an additional option (Add New Value) is added to the bottom of the list.

This widget allows the following additional options.

Option Description
required HTML5 required attribute
focus HTML5 autofocus attribute (mislabeled)
type: "text"

If the form element type is Text then an HTML5 text input will be displayed. There are a number of custom options for this element:

Option Description
autofocus HTML5 autofocus attribute
placeholder HTML5 placeholder attribute
provider Option List provider (described below). Renders HTML5 datalist
script Hyperscript script attribute
spellcheck HTML5 spellcheck attribute
style HTML5 style attribute
validator Remote validator URL for validating this widget from the server

Additional options are available depending on the data type used in the schema element connected to this widget.

schema.Type = Integer

If the associated schema is an Integer value, then the widget will render an HTML5 Numeric field with a default step=1. The following additional options are available:

Option Description
min HTML5 min attribute
max HTML5 max attribute
step HTML5 step attribute
schema.Type = Number

If the associated schema is an Number value, then the widget will render an HTML5 Numeric field. The following additional options are available:

Option Description
min HTML5 min attribute
max HTML5 max attribute
step HTML5 step attribute
schema.Type = String

If the associated schema is an String value, then the widget will render a different HTML5 field depending on the schema.Type.

Format Behavior
date Date field
datetime DateTime-Local field
email Email address field
time Time field
tel Phone number field
text (default) Plain text input
url URL field

In addition, the following options are available when rendering String values.

Option Description
maxlength HTML5 maxlength attribute
minlength HTML5 minlength attribute
pattern HTML5 pattern attribute
type: "textarea"

I the form element type is Textarea then an HTML5 textarea will be displayed. There are a number of custom options for this element:

Option Description
autofocus HTML5 autofocus attribute
rows HTML5 rows attribute
style HTML5 style attribute
minlength HTML5 minlength attribute
maxlength HTML5 maxlength attribute
pattern HTML5 pattern attribute
required HTML5 required attribute
showLimit If TRUE, then a Javascript counter will display the number of characters remaining before the maxlength limit is reached
type: "time"

This renders anHTML5 time input. It converts numbers and strings into a date-time value, then displays only the time portion in an input widget.

type: "toggle"

Toggle is a custom HTML widget defined for this library. It mimics the iOS toggle control in HTML. It returns either a true or false value when the form is submitted.

Option Value
text Text to appear next to the toggle button
true-text Text to appear next to the toggle button IF the value is TRUE
false-text Text to appear next to the toggle button IF the value is FALSE

Layouts

Layouts are a special kind of widget that contains other widgets. Layouts are used as a general container for a set of form elements. The child widgets inside of a layout are defined in the children property.

Layout widgets do not use any other custom options.

Layout-Group

Displays child elements in vertical groups, one above the other. Each child's Label is a heading above the remainder of its content.

Layout- Horizontal

Displays child elements horizontally. Each child's Label is placed above the widget.

This layout does not scale well in tight spaces and should be used sparingly.

Layout-Tabs

Displays child elements as tabs inside a tab container. Each child's Label is the label of the tab, and its Description is shown in text at the top of the tab panel.

Tabs are defined using W3C-ARIA roles for tabs: tablist, tab, and tabpanel

Layout-Vertical

Displays child elements in a vertical column, placing each child's Label in plain text above the widget, and its Description below the widget in small gray text.

This is the most common layout for basic HTML5 forms.

Option Lists

Certain widgets -- such as select boxes -- are able to display a list of options for the user to choose from. Options are defined in a few different ways:

Define Options Directly as an enum

Documentation TBD

Importing Options from the Application use a provider

Documentation TBD

JSON Marshaling/Unmarshalling

This package is intended to be used primarily by app developers to define forms in JSON configuration files. So, extra care is taken to make JSON marshaling / unmarshalling work well.

When building a form a JSON, all property names use lower camel case, with the first letter of the property name as lower case. If present, second and third words in the property name are capitalized

JSON Example

Here is a sample form configuration in JSON. This form is displayed in multiple tabs, adds/edits a number of values in the object (an "Album" in this case) and even has a place for file uploads.

{
	"label": "Album",
	"type":"layout-tabs",
	"children": [
		{
			"type":"layout-vertical",
			"label": "General",
			"children": [
				{"type":"text", "path":"label", "label":"Album Name"},
				{"type":"select", "path":"data.license", "label":"License", "options":{"required":true, "provider":"bandwagon-album.licenses"}},
				{"type":"date", "path":"data.releaseDate", "label":"Release Date"},
				{"type":"upload", "path":"iconUrl", "label":"Album Art", "options":{"accept":"image/*", "filename":"data.imageFilename", "delete":"/{{.StreamID}}/delete-icon"}},
				{"type":"toggle", "path":"isFeatured", "options":{"true-text":"Featured (shows on home page)", "false-text":"Featured?"}}
			]
		},
		{
			"type":"layout-vertical",
			"label": "Metadata",
			"children": [
				{"type":"textarea", "path":"summary", "label":"Sidebar Notes", "description":"Notes appear on the side of the album page. Markdown is allowed.", "options":{"rows":8, "showLimit":true}},
				{"type":"textarea", "path":"data.tags", "label":"Tags", "description":"Enter #Hashtags separated by spaces."}
			]
		},
		{
			"type":"layout-vertical",
			"label": "Links",
			"children": [
				{"type":"text", "path":"data.links.AMAZON", "options":{"placeholder":"Amazon Music"}},
				{"type":"text", "path":"data.links.APPLE", "options":{"placeholder":"Apple Music"}},
				{"type":"text", "path":"data.links.BANDCAMP", "options":{"placeholder":"Bandcamp"}},
				{"type":"text", "path":"data.links.GOOGLE", "options":{"placeholder":"Google Play"}},
				{"type":"text", "path":"data.links.SOUNDCLOUD", "options":{"placeholder":"Soundcloud"}},
				{"type":"text", "path":"data.links.SPOTIFY", "options":{"placeholder":"Spotify"}},
				{"type":"text", "path":"data.links.TIDAL", "options":{"placeholder":"Tidal"}},
				{"type":"text", "path":"data.links.YOUTUBE", "options":{"placeholder":"YouTube Music"}},
				{"type":"text", "path":"data.links.OTHER1", "options":{"placeholder":"Other"}},
				{"type":"text", "path":"data.links.OTHER2", "options":{"placeholder":"Other"}},
				{"type":"text", "path":"data.links.OTHER3", "options":{"placeholder":"Other"}}
			]
		},
		{
			"type":"layout-vertical",
			"label": "Colors",
			"children": [
				{"type":"colorpicker", "path":"data.color.body", "label":"Window Background"},
				{"type":"colorpicker", "path":"data.color.page", "label":"Page Background"},
				{"type":"colorpicker", "path":"data.color.button", "label":"Links and Buttons"}
			]
		}
	]
}

Project Status

This project is a work-in-progress. It is being used in production on large websites, but it is still under active development and is subject to change without notice.

If you're looking for a form library in Go, you should probably use something else that has a better stability guarantee.

With that said, if you have an idea for making this library better, send in a pull request. We're all in this together! 🤔

Documentation ¶

Overview ¶

Package form is a simplified implementation of JSON-Forms, which renders HTML forms from a JSON configuration.

Index ¶

Constants ¶

View Source
const NewItemIdentifier = "::NEWVALUE::"

NewItemIdentifier is a prefix appended to select options when the User wants to add a new item into a WritableLookupGroup

Variables ¶

This section is empty.

Functions ¶

func Editor ¶ added in v0.10.0

func Editor(schema schema.Schema, element Element, value any, lookupProvider LookupProvider, options ...string) (string, error)

Editor creates an in-place form and executes its "Editor" method

func LookupCodeSchema ¶ added in v0.22.9

func LookupCodeSchema() schema.Element

LookupCodeSchema defines the validation schema for LookupCodes

func SortLookupCodeByGroupThenLabel ¶ added in v0.15.0

func SortLookupCodeByGroupThenLabel(a LookupCode, b LookupCode) int

SortLookupCodeByGroupThenLabel is a sort function that works with the sort.Slice function.

func SortLookupCodeByLabel ¶ added in v0.15.0

func SortLookupCodeByLabel(a LookupCode, b LookupCode) int

SortLookupCodeByLabel is a sort function that works with the sort.Slice function.

func Use ¶ added in v0.20.0

func Use(name string, widget Widget)

Use adds a new widget into the widget registry.

func Viewer ¶ added in v0.10.0

func Viewer(schema schema.Schema, element Element, value any, lookupProvider LookupProvider) (string, error)

Viewer creates an in-place form and executes its "Viewer" method

Types ¶

type Element ¶ added in v0.9.1

type Element struct {
	Type        string    `json:"type"`                  // The kind of form element
	ID          string    `json:"id"`                    // The ID of the element (needed by some widgets)
	Path        string    `json:"path"`                  // Path to the data value displayed in for this form element
	Label       string    `json:"label,omitempty"`       // Short label to be displayed on the form element
	Description string    `json:"description,omitempty"` // Longer description text to be displayed on the form element
	Options     mapof.Any `json:"options,omitempty"`     // Additional custom properties defined by individual widgets
	Children    []Element `json:"children,omitempty"`    // Array of sub-form elements that may be displayed depending on the kind.
	ReadOnly    bool      `json:"readOnly,omitempty"`    // If true, then this element is read-only
}

Element defines a single form element, or a nested form layout. It can be serialized to and from a database.

func MustParse ¶ added in v0.4.0

func MustParse(data any) Element

MustParse guarantees that a value has been parsed into a Form, or else it panics the application.

func NewElement ¶ added in v0.9.1

func NewElement() Element

NewElement returns a fully populated Element object.

func Parse ¶ added in v0.3.9

func Parse(data any) (Element, error)

Parse attempts to convert a value into a Form. Currently supports map[string]any, []byte, string, and UnmarshalMaper interface.

func (*Element) AllElements ¶ added in v0.9.1

func (element *Element) AllElements() []*Element

AllElements returns pointers to all of the valid paths in this form

func (*Element) Edit ¶ added in v0.10.0

func (element *Element) Edit(form *Form, lookupProvider LookupProvider, value any, b *html.Builder) error

Edit returns an HTML form that can edit this element.

func (*Element) Encoding ¶ added in v0.20.0

func (element *Element) Encoding() string

Encoding returns the form encoding required by this widget.

func (*Element) GetSchema ¶ added in v0.20.0

func (element *Element) GetSchema(s *schema.Schema) schema.Element

GetSchema finds and returns the schema.Element associated with this Element path

func (*Element) GetSliceOfString ¶ added in v0.9.1

func (element *Element) GetSliceOfString(value any, s *schema.Schema) []string

GetSliceOfString rturns a slice of strings for a provided path.

func (*Element) GetString ¶ added in v0.9.1

func (element *Element) GetString(value any, s *schema.Schema) string

GetString returns the value of the element at the provided path. If the schema is present, then it is used to resolve the value. If the schema is not present, then the value is returned using path lookup instead.

func (Element) IsEmpty ¶ added in v0.17.12

func (element Element) IsEmpty() bool

IsEmpty returns TRUE if the element is not defined

func (*Element) UnmarshalMap ¶ added in v0.9.1

func (element *Element) UnmarshalMap(data map[string]any) error

UnmarshalMap parses data from a generic structure (mapof.Any) into a Form record.

func (*Element) View ¶ added in v0.10.0

func (element *Element) View(form *Form, lookupProvider LookupProvider, value any, b *html.Builder) error

View returns static HTML that displays the value of this element.

func (*Element) Widget ¶ added in v0.10.0

func (element *Element) Widget() (Widget, error)

Widget retrieves the specific widget object for this Element

type Form ¶

type Form struct {
	Schema  schema.Schema
	Element Element
	Options []string
}

Form defines all of the data for this JSON form. This can be marshalled/unmarshalled as JSON, or entered directly into Go source code.

func New ¶

func New(schema schema.Schema, element Element, options ...string) Form

New returns a fully initialized Form object (with all required values)

func (*Form) BuildEditor ¶ added in v0.10.0

func (form *Form) BuildEditor(value any, lookupProvider LookupProvider, builder *html.Builder) error

BuildEditor generates an editable view of this form

func (*Form) BuildViewer ¶ added in v0.10.0

func (form *Form) BuildViewer(value any, lookupProvider LookupProvider, builder *html.Builder) error

BuildViewer generates a read-only view of this form

func (*Form) Editor ¶ added in v0.10.0

func (form *Form) Editor(value any, lookupProvider LookupProvider) (string, error)

Editor returns a fully populated HTML form for this Form

func (*Form) Encoding ¶ added in v0.20.0

func (form *Form) Encoding() string

Encoding returns the "enctype" attribute for the form. Default is ""

func (*Form) OptionInt ¶ added in v0.24.0

func (form *Form) OptionInt(name string) int

OptionInt returns the integer value of a Form option.

func (*Form) OptionString ¶ added in v0.24.0

func (form *Form) OptionString(name string) string

OptionString returns the string value of a Form option.

func (*Form) SetURLValues ¶ added in v0.22.12

func (form *Form) SetURLValues(object any, values url.Values, lookupProvider LookupProvider) error

SetURLValues applies all of the data from the value map into the target object

func (*Form) Viewer ¶ added in v0.10.0

func (form *Form) Viewer(value any, lookupProvider LookupProvider) (string, error)

Viewer returns a read-only HTML representation of this Form

type LookupCode ¶ added in v0.9.1

type LookupCode struct {
	Value       string `json:"value,omitempty"       form:"value"       bson:"value,omitempty"`       // Internal value of the LookupCode
	Label       string `json:"label,omitempty"       form:"label"       bson:"label,omitempty"`       // Human-friendly label/name of the LookupCode
	Description string `json:"description,omitempty" form:"description" bson:"description,omitempty"` // Optional long description of the LookupCode
	Icon        string `json:"icon,omitempty"        form:"icon"        bson:"icon,omitempty"`        // Optional icon to use when displaying the LookupCode
	Group       string `json:"group,omitempty"       form:"group"       bson:"group,omitempty"`       // Optiional grouping to use when displaying the LookupCode
	Href        string `json:"href,omitempty"        form:"href"        bson:"href,omitempty"`        // Optional URL to use when using this LookupCode
}

LookupCode represents a single value/label pair to be used in place of Enums for optional lists.

func AsLookupCode ¶ added in v0.16.0

func AsLookupCode[T LookupCodeMaker](maker T) LookupCode

AsLookupCode is a helper function that converts any object that implements the LookupCodeMaker interface into a form.LookupCode

func GetLookupCodes ¶ added in v0.9.1

func GetLookupCodes(element *Element, schemaElement schema.Element, lookupProvider LookupProvider) ([]LookupCode, bool)

GetLookupCodes returns a list of LookupCodes derived from: 1) an "enum" (string or slice-of-lookupCode) in the form element, 2) a "datasource" value that is looked up in the lookupProvider 3) a value enumerated in the schema

The boolean value is TRUE if this comes from a WritableLookupGroup

func NewLookupCode ¶ added in v0.9.1

func NewLookupCode(value string) LookupCode

NewLookupCode creates a new LookupCode from a string

func ParseLookupCode ¶ added in v0.15.2

func ParseLookupCode(value any) LookupCode

ParseLookupCode converts a number of values into a LookupCode, including: LookupCode, , mapof.Any, mapof.String, map[string]any, map[string]string, and string. For strings, the string value is used for both the .Value and .Label If no compatible type is found, then an empty LookupCode is returned.

func (*LookupCode) GetPointer ¶ added in v0.15.5

func (lookupCode *LookupCode) GetPointer(name string) (any, bool)

GetPointer returns pointers to the named property of this LookupCode

func (LookupCode) GetStringOK ¶ added in v0.25.2

func (lookupCode LookupCode) GetStringOK(name string) (string, bool)

GetStringOK returns the string value of each property of this LookupCode

func (LookupCode) ID ¶ added in v0.12.0

func (lookupCode LookupCode) ID() string

ID returns the unique ID of the LookupCode, allowing them to be used as a set.Value

func (*LookupCode) SetString ¶ added in v0.25.2

func (lookupCode *LookupCode) SetString(name string, value string) bool

SetString sets a string value in this LookupCode

type LookupCodeMaker ¶ added in v0.16.0

type LookupCodeMaker interface {
	// LookupCode returns the data from current object in the form of a form.LookupCode
	LookupCode() LookupCode
}

LookupCodeMaker is an interface that wraps the LookupCode method

type LookupGroup ¶ added in v0.13.1

type LookupGroup interface {
	Get() []LookupCode
}

LookupGroup is an read-only interface that returns a list of LookupCodes

type LookupProvider ¶ added in v0.9.1

type LookupProvider interface {
	Group(name string) LookupGroup
}

LookupProvider is an external object that can inject LookupCodes based on their URL.

type ReadOnlyLookupGroup ¶ added in v0.13.1

type ReadOnlyLookupGroup []LookupCode

ReadOnlyLookupGroup is a simple implementation of the LookupGroup interface that returns a static list of LookupCodes.

func NewReadOnlyLookupGroup ¶ added in v0.13.1

func NewReadOnlyLookupGroup(codes ...LookupCode) ReadOnlyLookupGroup

NewReadOnlyLookupGroup returns a fully initialized ReadOnlyLookupGroup

func (ReadOnlyLookupGroup) Get ¶ added in v0.13.1

func (group ReadOnlyLookupGroup) Get() []LookupCode

Get returns the list of LookupCodes

func (ReadOnlyLookupGroup) Value ¶ added in v0.22.8

func (group ReadOnlyLookupGroup) Value(value string) LookupCode

Value returns the LookupCode that matches the provided value, and an empty LookupCode if no match is found.

type URLValueSetter ¶ added in v0.25.2

type URLValueSetter interface {

	// SetURLValue applies applies all values from a url.Values slice to the provided object.
	SetURLValue(form *Form, element *Element, object any, values url.Values) error
}

URLValueSetter interface wraps the SetURLValue method, which applies the values from a url.Values slice into an arbitrary object.

type UnmarshalMaper ¶ added in v0.6.4

type UnmarshalMaper interface {

	// UnmarshalMap returns a value in the format map[string]interface
	UnmarshalMap() map[string]any
}

UnmarshalMaper wraps the UnmarshalMap interface

type Widget ¶ added in v0.10.0

type Widget interface {
	View(form *Form, element *Element, lookupProvider LookupProvider, value any, builder *html.Builder) error
	Edit(form *Form, element *Element, lookupProvider LookupProvider, value any, builder *html.Builder) error
	ShowLabels() bool
	Encoding(element *Element) string
}

Widget defines a data type that can be included in a form

type WritableLookupGroup ¶ added in v0.13.1

type WritableLookupGroup interface {
	LookupGroup
	Add(name string) (string, error)
}

WritableLookupGroup is a read-write interface that returns a list of LookupCodes, and can add new codes to the list.

Directories ¶

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL