formulate

package module
v1.0.6 Latest Latest
Warning

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

Go to latest
Published: Jul 1, 2021 License: MIT Imports: 12 Imported by: 4

README

formulate Godoc

a HTML form builder and HTTP request to struct parser.

Example (Encoder)
type Address struct {
    HouseName       string `help:"You can leave this blank."`
    AddressLine1    string
    AddressLine2    string
    Postcode        string
    TelephoneNumber Tel
    CountryCode     string `pattern:"[A-Za-z]{3}"`
}

buf := new(bytes.Buffer)

address := Address{
    AddressLine1: "Fake Street",
}

encoder := NewEncoder(buf, nil)
encoder.SetFormat(true)

if err := encoder.Encode(&address); err != nil {
    panic(err)
}

fmt.Println(buf.String())

Output:

<div>
  <fieldset>
    <div>
      <label for="HouseName">
        House Name
      </label>
      <div>
        <input type="text" name="HouseName" id="HouseName" value=""/>
        <div>You can leave this blank.</div>
      </div>
    </div>
    <div>
      <label for="AddressLine1">
        Address Line 1
      </label>
      <div>
        <input type="text" name="AddressLine1" id="AddressLine1" value="Fake Street"/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="AddressLine2">
        Address Line 2
      </label>
      <div>
        <input type="text" name="AddressLine2" id="AddressLine2" value=""/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="Postcode">
        Postcode
      </label>
      <div>
        <input type="text" name="Postcode" id="Postcode" value=""/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="TelephoneNumber">
        Telephone Number
      </label>
      <div>
        <input type="tel" name="TelephoneNumber" id="TelephoneNumber" value=""/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="CountryCode">
        Country Code
      </label>
      <div>
        <input type="text" name="CountryCode" id="CountryCode" value="" pattern="[A-Za-z]{3}"/>
        <div></div>
      </div>
    </div>
  </fieldset>
</div>
Example (Decoder)
type Address struct {
    HouseName       string `help:"You can leave this blank."`
    AddressLine1    string
    AddressLine2    string
    Postcode        string
    TelephoneNumber Tel
    CountryCode     string `pattern:"[A-Za-z]{3}"`
}

// formValues - usually these would come from *http.Request.Form!
formValues := url.Values{
    "HouseName":       {"1 Example Road"},
    "AddressLine1":    {"Fake Town"},
    "AddressLine2":    {"Fake City"},
    "Postcode":        {"Postcode"},
    "TelephoneNumber": {"012345678910"},
    "CountryCode":     {"GBR"},
}

var address Address

decoder := NewDecoder(formValues)

if err := decoder.Decode(&address); err != nil {
    panic(err)
}

fmt.Printf("House Name: %s\n", address.HouseName)
fmt.Printf("Line 1: %s\n", address.AddressLine1)
fmt.Printf("Line 2: %s\n", address.AddressLine2)
fmt.Printf("Postcode: %s\n", address.Postcode)
fmt.Printf("Telephone: %s\n", address.TelephoneNumber)
fmt.Printf("CountryCode: %s\n", address.CountryCode)

Output:

House Name: 1 Example Road
Line 1: Fake Town
Line 2: Fake City
Postcode: Postcode
Telephone: 012345678910
CountryCode: GBR

Documentation

Overview

Package formulate is a set of tools for building HTML forms from structs, and parsing HTTP form values back into structs.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AppendClass

func AppendClass(n *html.Node, classes ...string)

AppendClass adds a class to a HTML node.

func BuildBoolField added in v1.0.1

func BuildBoolField(v reflect.Value, key string, field StructField) *html.Node

func BuildField added in v1.0.2

func BuildField(v reflect.Value, key string, field StructField, parent *html.Node, decorator Decorator, showConditions map[string]ShowConditionFunc) error

func BuildHelpText added in v1.0.2

func BuildHelpText(parent *html.Node, field StructField, decorator Decorator)

func BuildLabel added in v1.0.2

func BuildLabel(label string, parent *html.Node, field StructField, decorator Decorator)

func BuildNumberField added in v1.0.1

func BuildNumberField(v reflect.Value, key string, field StructField) *html.Node

func BuildRadioButtons added in v1.0.1

func BuildRadioButtons(r RadioList, key string, field StructField, decorator Decorator) *html.Node

func BuildSelectField added in v1.0.1

func BuildSelectField(s Select, key string, field StructField) *html.Node

func BuildStringField added in v1.0.1

func BuildStringField(v reflect.Value, key string, field StructField) *html.Node

func BuildTimeField added in v1.0.1

func BuildTimeField(t time.Time, key string, field StructField) *html.Node

func HasAttribute

func HasAttribute(n *html.Node, attr string) bool

HasAttribute returns true if n has the attribute named attr.

func OptGroup added in v1.0.3

func OptGroup(name string) *string

Types

type BoolNumber

type BoolNumber int

BoolNumber represents an int (0 or 1) which should actually be rendered as a checkbox. It is provided here as a convenience, as many structures use 0 or 1 to represent booleans values.

func (*BoolNumber) Bool

func (bn *BoolNumber) Bool() bool

Bool returns true if the underlying value is 1.

func (BoolNumber) DecodeFormValue

func (bn BoolNumber) DecodeFormValue(form url.Values, name string, values []string) (reflect.Value, error)

DecodeFormValue implements the CustomDecoder interface.

type Condition

type Condition bool

Condition are optional booleans for Options.

func NewCondition

func NewCondition(b bool) *Condition

NewCondition creates a new condition based on a bool value.

type CustomDecoder

type CustomDecoder interface {
	// DecodeFormValue is passed the whole form values, the name of the element that it is decoding,
	// and the values for that specific element. It must return a reflect.Value of equal type to the
	// type which is implementing the CustomDecoder interface. If err != nil, the error will propagate
	// back through to the Decode() call.
	DecodeFormValue(form url.Values, name string, values []string) (reflect.Value, error)
}

CustomDecoder allows for custom decoding behavior to be specified for an element. If a type implements the CustomDecoder interface, DecodeFormValue is called in place of any other decoding behavior.

type CustomEncoder

type CustomEncoder interface {
	// BuildFormElement is passed the key of the form element as computed by formulate,
	// the parent node of the element, the field of the struct
	// that is currently being rendered, and the form's decorator.
	// Note that the built element must be appended to the parent or it will not be shown in the form!
	// Errors returned from BuildFormElement propagate back through to the formulate.Encoder.Encode call.
	BuildFormElement(key string, parent *html.Node, field StructField, decorator Decorator) error
}

CustomEncoder allows for custom rendering behavior of a type to be implemented. If a type implements the CustomEncoder interface, BuildFormElement is called in place of any other formulate rendering behavior for inputs. The label and help text of the element will still be rendered within the row as normal.

type Decorator

type Decorator interface {
	// RootNode decorates the root <div> of the returned HTML.
	RootNode(n *html.Node)
	// Fieldset decorates each <fieldset>. Fieldsets are created for each
	// non-anonymous struct within the encoded data structure.
	Fieldset(n *html.Node, field StructField)
	// Row decorates the parent of each label, input and help text, for each field within the encoded data structure.
	Row(n *html.Node, field StructField)
	// FieldWrapper decorates the div which wraps the input and help text within a form
	FieldWrapper(n *html.Node, field StructField)
	// Label decorates the <label> for the form element
	Label(n *html.Node, field StructField)
	// HelpText decorates the text which is displayed below each form element.
	// The HelpText is generated from the "help" struct tag.
	HelpText(n *html.Node, field StructField)
	// TextField decorates an <input type="text">
	TextField(n *html.Node, field StructField)
	// NumberField decorates an <input type="number"> or equivalent (e.g. Tel)
	NumberField(n *html.Node, field StructField)
	// CheckboxField decorates an <input type="checkbox">, displayed for boolean values.
	CheckboxField(n *html.Node, field StructField)
	// TextareaField decorates a <textarea> tag.
	TextareaField(n *html.Node, field StructField)
	// TimeField decorates an <input type="datetime-local"> used to represent time values.
	TimeField(n *html.Node, field StructField)
	// SelectField decorates a <select> dropdown
	SelectField(n *html.Node, field StructField)
	// RadioButton decorates an individual <input type="radio">
	RadioButton(n *html.Node, field StructField)
}

Decorator is used to customise node elements that are built by the HTMLEncoder. Custom decorators can be passed into the HTMLEncoder. If no decorator is specified, a nil decorator is used. This applies no decoration to the output HTML.

type Email

type Email string

Email represents an <input type="email">

type HTMLEncoder

type HTMLEncoder struct {
	// contains filtered or unexported fields
}

HTMLEncoder is used to generate a HTML form from a given struct.

func NewEncoder

func NewEncoder(w io.Writer, decorator Decorator) *HTMLEncoder

NewEncoder returns a HTMLEncoder which outputs to w. A Decorator can be passed to NewEncoder, which will then be used to style the outputted HTML. If nil is passed in, no decorator is used, and a barebones HTML form will be returned.

Example
type Address struct {
	HouseName       string `help:"You can leave this blank."`
	AddressLine1    string
	AddressLine2    string
	Postcode        string
	TelephoneNumber Tel
	CountryCode     string `pattern:"[A-Za-z]{3}"`
}

buf := new(bytes.Buffer)

address := Address{
	AddressLine1: "Fake Street",
}

encoder := NewEncoder(buf, nil)
encoder.SetFormat(true)

if err := encoder.Encode(&address); err != nil {
	panic(err)
}

fmt.Println(buf.String())
Output:

<div>
  <fieldset>
    <div>
      <label for="HouseName">
        House Name
      </label>
      <div>
        <input type="text" name="HouseName" id="HouseName" value=""/>
        <div>
          You can leave this blank.
        </div>
      </div>
    </div>
    <div>
      <label for="AddressLine1">
        Address Line 1
      </label>
      <div>
        <input type="text" name="AddressLine1" id="AddressLine1" value="Fake Street"/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="AddressLine2">
        Address Line 2
      </label>
      <div>
        <input type="text" name="AddressLine2" id="AddressLine2" value=""/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="Postcode">
        Postcode
      </label>
      <div>
        <input type="text" name="Postcode" id="Postcode" value=""/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="TelephoneNumber">
        Telephone Number
      </label>
      <div>
        <input type="tel" name="TelephoneNumber" id="TelephoneNumber" value=""/>
        <div></div>
      </div>
    </div>
    <div>
      <label for="CountryCode">
        Country Code
      </label>
      <div>
        <input type="text" name="CountryCode" id="CountryCode" value="" pattern="[A-Za-z]{3}"/>
        <div></div>
      </div>
    </div>
  </fieldset>
</div>

func (*HTMLEncoder) AddShowCondition

func (h *HTMLEncoder) AddShowCondition(key string, fn ShowConditionFunc)

AddShowCondition allows you to determine visibility of certain form elements. For example, given the following struct:

type Example struct {
  Name string
  SecretOption bool `show:"adminOnly"`
}

If you wanted to make the SecretOption field only show to admins, you would call AddShowCondition as follows:

AddShowCondition("adminOnly", func() bool {
   // some code that determines if we are 'admin'
})

You can add multiple ShowConditions, but they must have different keys.

func (*HTMLEncoder) Encode

func (h *HTMLEncoder) Encode(i interface{}) error

Encode takes a struct (or struct pointer) and produces a HTML form from all elements in the struct. The encoder deals with most simple types and structs, but more complex types (maps, slices, arrays) will render as a JSON blob in a <textarea>.

The rendering behavior of any element can be replaced by implementing the CustomEncoder interface.

func (*HTMLEncoder) SetFormat

func (h *HTMLEncoder) SetFormat(b bool)

SetFormat tells the HTMLEncoder to output formatted HTML. Formatting is provided by the https://github.com/yosssi/gohtml package.

type HTTPDecoder

type HTTPDecoder struct {
	// contains filtered or unexported fields
}

HTTPDecoder takes a set of url values and decodes them.

func NewDecoder

func NewDecoder(form url.Values) *HTTPDecoder

NewDecoder creates a new HTTPDecoder.

Example
type Address struct {
	HouseName       string `help:"You can leave this blank."`
	AddressLine1    string
	AddressLine2    string
	Postcode        string
	TelephoneNumber Tel
	CountryCode     string `pattern:"[A-Za-z]{3}"`
}

// formValues - usually these would come from *http.Request.Form!
formValues := url.Values{
	"HouseName":       {"1 Example Road"},
	"AddressLine1":    {"Fake Town"},
	"AddressLine2":    {"Fake City"},
	"Postcode":        {"Postcode"},
	"TelephoneNumber": {"012345678910"},
	"CountryCode":     {"GBR"},
}

var address Address

decoder := NewDecoder(formValues)

if err := decoder.Decode(&address); err != nil {
	panic(err)
}

fmt.Printf("House Name: %s\n", address.HouseName)
fmt.Printf("Line 1: %s\n", address.AddressLine1)
fmt.Printf("Line 2: %s\n", address.AddressLine2)
fmt.Printf("Postcode: %s\n", address.Postcode)
fmt.Printf("Telephone: %s\n", address.TelephoneNumber)
fmt.Printf("CountryCode: %s\n", address.CountryCode)
Output:

House Name: 1 Example Road
Line 1: Fake Town
Line 2: Fake City
Postcode: Postcode
Telephone: 012345678910
CountryCode: GBR

func (*HTTPDecoder) Decode

func (h *HTTPDecoder) Decode(data interface{}) error

Decode a the given values into a provided interface{}. Note that the underlying value must be a pointer.

type Option

type Option struct {
	Value interface{}
	Label string
	Group *string

	Disabled bool
	Checked  *Condition
	Attr     []html.Attribute
}

Option represents an option in Select inputs and Radio inputs.

type Password

type Password string

Password represents an <input type="password">

type RadioList

type RadioList interface {
	CustomDecoder

	RadioOptions() []Option
}

RadioList represents a list of <input type="radio">. Radio lists must implement their own decoder.

type Raw

type Raw []byte

Raw is byte data which should be rendered as a string inside a textarea.

func (Raw) BuildFormElement

func (r Raw) BuildFormElement(key string, parent *html.Node, field StructField, decorator Decorator) error

BuildFormElement implements the CustomEncoder interface.

type Select

type Select interface {
	// SelectMultiple indicates whether multiple options can be selected at once.
	SelectMultiple() bool

	// SelectOptions are the available options
	SelectOptions() []Option
}

Select represents a HTML <select> element.

type ShowConditionFunc

type ShowConditionFunc func() bool

ShowConditionFunc is a function which determines whether or not to show a form element. See: HTMLEncoder.AddShowCondition

type StructField

type StructField struct {
	reflect.StructField
}

StructField is a wrapper around the reflect.StructField type. The rendering behavior of form elements is controlled by Struct Tags. The following tags are currently available:

  • name (e.g. name:"Phone Number") - this overwrites the name used in the label. This value can be left empty.
  • help (e.g. help:"Enter your phone number, including area code") - this text is displayed alongside the input field as a prompt.
  • show (e.g. show:"adminOnly") - controls visibility of elements. See HTMLEncoder.AddShowCondition for more details. if "contents" is used, the field is shown and the parent fieldset (if any) will be omitted.
  • type (e.g. type:"tel") - sets the HTML input "type" attribute
  • elem (elem:"textarea") - used to specify that a text input should use a <textarea> rather than an input field.
  • min (e.g. min:"0") - minimum value for number inputs
  • max (e.g. max:"10") - maximum value for number inputs
  • step (e.g. step:"0.1") - step size for number inputs
  • pattern (e.g. pattern:"[a-z]+" - regex pattern for text inputs

These can all be used in combination with one another in a struct field. A full example of the above types is:

type Address struct {
    HouseNumber          int `min:"0" help:"Please enter your house number" name:"House Number (if any)"
    AddressLine1         string
    DeliveryInstructions string `elem:"textarea"`
    CountryCode          string `pattern:"[A-Za-z]{3}"`
}

func (StructField) BuildFieldset

func (sf StructField) BuildFieldset() bool

BuildFieldset determines whether a given struct should be inside its own fieldset. Use the Struct Tag show:"contents" to indicate that a fieldset should not be built for this struct.

func (StructField) Elem

func (sf StructField) Elem() string

Elem returns the element to be used. Currently the only supported value is <textarea>. <input> will be used if not specified.

func (StructField) GetHelpText

func (sf StructField) GetHelpText() string

GetHelpText returns the help text for the field.

func (StructField) GetName

func (sf StructField) GetName() string

GetName returns the name of the StructField, taking into account tag name overrides.

func (StructField) HasMax

func (sf StructField) HasMax() bool

HasMax determines if a StructField has a maximum value

func (StructField) HasMin

func (sf StructField) HasMin() bool

HasMin determines if a StructField has a minimum value

func (StructField) HasStep

func (sf StructField) HasStep() bool

HasStep determines if a StructField has a step value

func (StructField) Hidden

func (sf StructField) Hidden(showConditions map[string]ShowConditionFunc) bool

Hidden determines if a StructField is hidden based on the showConditions.

func (StructField) InputType

func (sf StructField) InputType(original string) string

InputType returns the HTML <input> element type attribute

func (StructField) Max

func (sf StructField) Max() string

Max is the maximum value of the StructField

func (StructField) Min

func (sf StructField) Min() string

Min is the minimum value of the StructField

func (StructField) Pattern

func (sf StructField) Pattern() string

func (StructField) Placeholder added in v1.0.2

func (sf StructField) Placeholder() string

func (StructField) Required added in v1.0.2

func (sf StructField) Required() bool

func (StructField) Step

func (sf StructField) Step() string

Step value of the StructField

type Tel

type Tel string

Tel represents an <input type="tel">

type URL

type URL string

URL represents an <input type="url">

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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