forms

package module
v0.0.0-...-655b45b Latest Latest
Warning

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

Go to latest
Published: Nov 29, 2020 License: MIT Imports: 8 Imported by: 0

README

forms Build Status Coverage Status Go Report Card GoDoc

I found no sufficient forms library in GO, so I decided to write one by my self. ;-)

The main goal is separate validation from field rendering, so you won't find here silly structures like:

Field{Required: True}

Validate if user entered any data should be done in validation, and if you want <input /> to have a required attribute, you need to set this in field's attributes.

Usage

Here is a basic usage for forms. Keep in mind, that validation is separated from field, and inputs and albels HTML attributes are also separated.

import (
	"fmt"
	"net/http"

	"github.com/Alkemic/forms"
)

func someView(w http.ResponseWriter, r *http.Request) {
	form := forms.New(
		map[string]*forms.Field{
			"email": &forms.Field{
				Validators: []forms.Validator{
					&forms.Required{},
				},
			},
			"password": &forms.Field{
				Type: &forms.InputPassword{},
				Validators: []forms.Validator{
					&forms.Required{},
				},
			},
		},
		forms.Attributes{"id": "login-form"},
	)

	if form.IsValid(r.PostForm) {
		// if valid you can access cleaned data in attribute CleanedData
		for k, v := range form.CleanedData{
			fmt.Println("key:", k, "value:", v)
		}
	} else {
		// else not ;-)
	}
}

I've decided to don't write whole form rendering method, because, let's be honest, it won't give level of control over form that we need and in the end you will have to do it by yourself. Insted of there are methods that will help you with displaying form.

{{.Form.OpenTag}}
{{if .Form.Fields.email.HasErrors}}
    {{.Form.Fields.email.RenderErrors}}
{{end}}
{{.Form.Fields.email.RenderLabel}}
{{.Form.Fields.email.Render}}

{{if .Form.Fields.password.HasErrors}}
    {{.Form.Fields.password.RenderErrors}}
{{end}}
{{.Form.Fields.password.RenderLabel}}
{{.Form.Fields.password.Render}}
{{.Form.CloseTag}}

Eventually you can render errors by yourself

{{if .Form.Fields.email.HasErrors}}
    <ul>
        {{range .Form.Fields.email.Errors}}
        <li class="error">{{.}}</li>
        {{end}}
    </ul>
{{end}}

Installation

As usual, no magic here:

$ go get github.com/Alkemic/forms

Field

Fields are representation of single field in form, it's a container for validators and attributes.

Field{
	Type: &Input{},
	Validators: []Validator{
		&Required{},
	},
	Attributes{"id": "test"},
	Label: "Test label",
	LabelAttributes: Attributes{
		"required": "required",
		"id":   "test",
		"attr": "value",
	},
}

When fields label is rendered (field.RenderLabel) attribute for is automaticly added as well as attribute id to field.

Field types

Types are responsible for field behavior: rendering, cleaning data and giving information if field accept multiple value. Generally you should not access fields type directly, as its used by field.

Cleaning data

The incoming data need to be cleaned after succesful validation, and before we give them to user. By clean, we mean that we convert them to format/ All of this transformation are done by method CleanData on Type. For example, when we crate Field with type NumberInput in form.CleanedData we find a number (int), for MultiSelect we find a slice with all selected values.

TODO

Big fat note: this library is under development, and it's API may or may not change. Currently this library works, but I don't recomend this for prodution or even thinking about production usage. ;-)

  • Field rendering
  • Initial data support
  • Internationalization
  • Field types (inc. types introduced in HTML5)
    • Input
    • Textarea
    • Radio
    • Select
    • Email
    • Number
    • Color
    • File
    • Hidden
    • Image
    • Month
    • Password
    • Range
    • Telephone
    • Time
    • URL
    • Week
    • Date
    • Datetime
    • Datetime-local
  • Validators
    • Regexp
    • Required
    • Email
    • MinLength
    • MaxLength
    • InSlice
    • MinValue
    • MaxValue
    • URL
    • Date
    • Time
    • DateTime

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Attributes

type Attributes map[string]interface{}

Attributes is structure that contains forms or fields attributes

type Checkbox

type Checkbox struct {
	*Input
}

Checkbox is checkbox input type

func (*Checkbox) CleanData

func (t *Checkbox) CleanData(values []string) interface{}

CleanData returns cleaned values for checkbox

func (*Checkbox) Render

func (t *Checkbox) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered checkbox input

type Choice

type Choice struct {
	Value string
	Label string
}

Choice is used to store choices in field I choose to use type here because we need ordering of choices

type Data

type Data map[string]interface{}

Data is structure in which we store cleaned and initial data

type Email

type Email struct{}

Email validator checks if given value is proper email

func (*Email) IsValid

func (v *Email) IsValid(values []string) (bool, []string)

IsValid checks is entered data are correct

type Field

type Field struct {
	Name string

	Label           string
	LabelAttributes Attributes

	Choices      []Choice
	Value        []string
	InitialValue interface{}
	Type         Type
	Attributes   Attributes

	Validators []Validator
	Errors     []string
}

Field represent single field in form and its validatorss

func (*Field) HasErrors

func (f *Field) HasErrors() bool

HasErrors returns information if there are validation errors in this field

func (*Field) IsValid

func (f *Field) IsValid(values []string) (isValid bool)

IsValid do data validation

func (*Field) Render

func (f *Field) Render() template.HTML

Render field (in matter of fact, only passing through to render method on type)

func (*Field) RenderErrors

func (f *Field) RenderErrors() template.HTML

RenderErrors render all errors as list (<ul>) with class "errors"

func (*Field) RenderLabel

func (f *Field) RenderLabel() template.HTML

RenderLabel render label for field

type Form

type Form struct {
	// Keeps all the fields
	Fields map[string]*Field

	// Form attributes
	Attributes Attributes

	// Data that are used in validation
	IncomingData url.Values
	// After validation is done we put data here
	CleanedData Data
	// Initial data are used before form validation
	InitialData Data
}

Form is structure that hold all fields, data, and

func New

func New(fields map[string]*Field, attrs Attributes) *Form

New is shorthand, and preferred way, to create new form. Main difference is that, this approach add field name, basing on key in map, to a field instance Example

form := forms.New(
    map[string]*forms.Field{
        "field1": &forms.Field{},
        "field2": &forms.Field{},
    },
    forms.Attributes{"id": "test"},
)

func (*Form) Clear

func (f *Form) Clear()

Clear clears error and data on fields in form

func (*Form) CloseTag

func (f *Form) CloseTag() template.HTML

CloseTag render closing tag for form

func (*Form) IsValid

func (f *Form) IsValid(data url.Values) bool

IsValid validate all fields and if all is correct assign cleaned data from every field to forms CleanedData attribute

func (*Form) IsValidMap

func (f *Form) IsValidMap(values map[string]interface{}) bool

IsValidMap populates data from map. It accepts map of string/strings with keys as field names.

func (*Form) OpenTag

func (f *Form) OpenTag() template.HTML

OpenTag render opening tag of the form with given attributes

func (*Form) SetInitial

func (f *Form) SetInitial(data Data)

SetInitial sets initial data on form

type InSlice

type InSlice struct {
	Values []string
}

InSlice validator checks if given value is in slice

validator := &MaxLength{[]string{"ham", "spam", "eggs"}]}

func (*InSlice) IsValid

func (v *InSlice) IsValid(values []string) (bool, []string)

IsValid checks is entered data are correct

type Input

type Input struct{}

Input is basic input type

func (*Input) CleanData

func (i *Input) CleanData(values []string) interface{}

CleanData returns cleaned values for basic input

func (*Input) IsMultiValue

func (i *Input) IsMultiValue() bool

IsMultiValue returns if basic input allow multiple values

func (*Input) Render

func (i *Input) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered basic input

type InputDate

type InputDate struct {
	*Input
}

InputDate is date input type

func (*InputDate) Render

func (t *InputDate) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered date input

type InputDateTime

type InputDateTime struct {
	*Input
}

InputDateTime is date datetime (uses datetime-local) input type

func (*InputDateTime) Render

func (t *InputDateTime) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered datetime input

type InputEmail

type InputEmail struct {
	*Input
}

InputEmail is email input type

func (*InputEmail) Render

func (t *InputEmail) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered email input

type InputMonth

type InputMonth struct {
	*Input
}

InputMonth is month input type

func (*InputMonth) Render

func (t *InputMonth) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered month input

type InputNumber

type InputNumber struct{}

InputNumber is number input type

func (*InputNumber) CleanData

func (t *InputNumber) CleanData(values []string) interface{}

CleanData returns cleaned values for number input

func (*InputNumber) IsMultiValue

func (t *InputNumber) IsMultiValue() bool

IsMultiValue returns if numeric input allow multiple values

func (*InputNumber) Render

func (t *InputNumber) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered number input

type InputPassword

type InputPassword struct {
	*Input
}

InputPassword is password input type

func (*InputPassword) Render

func (t *InputPassword) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered password input

type InputSearch

type InputSearch struct {
	*Input
}

InputSearch is tel search type

func (*InputSearch) Render

func (t *InputSearch) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered search input

type InputTel

type InputTel struct {
	*Input
}

InputTel is tel input type

func (*InputTel) Render

func (t *InputTel) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered tel input

type InputTime

type InputTime struct {
	*Input
}

InputTime is date input type

func (*InputTime) Render

func (t *InputTime) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered time input

type InputURL

type InputURL struct {
	*Input
}

InputURL is url input type

func (*InputURL) Render

func (t *InputURL) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered url input

type InputWeek

type InputWeek struct {
	*Input
}

InputWeek is week input type

func (*InputWeek) Render

func (t *InputWeek) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered week input

type MaxLength

type MaxLength struct {
	Max int
}

MaxLength validator checks if given values length doesn't exceed given value

validator := &MaxLength{32}

func (*MaxLength) IsValid

func (v *MaxLength) IsValid(values []string) (bool, []string)

IsValid checks is entered data are correct

type MinLength

type MinLength struct {
	Min int
}

MinLength validator checks if given values length is under value

validator := &MaxLength{32}

func (*MinLength) IsValid

func (v *MinLength) IsValid(values []string) (bool, []string)

IsValid checks is entered data are correct

type Radio

type Radio struct{}

Radio is radio input type

func (*Radio) CleanData

func (i *Radio) CleanData(values []string) interface{}

CleanData returns cleaned values for radio

func (*Radio) IsMultiValue

func (i *Radio) IsMultiValue() bool

IsMultiValue returns if radio input allow multiple values

func (*Radio) Render

func (i *Radio) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered radio input

type Regexp

type Regexp struct {
	Pattern string
}

Regexp validator checks if given value match pattern

validator := &Regexp{"\d{4}.\d{2}.\d{2} \d{2}:\d{2}:\d{2}"}

func (*Regexp) IsValid

func (r *Regexp) IsValid(values []string) (bool, []string)

IsValid checks is entered data are correct

type Required

type Required struct{}

Required is validator that require some data input

func (*Required) IsValid

func (r *Required) IsValid(values []string) (bool, []string)

IsValid checks is entered data are correct

type Textarea

type Textarea struct {
	Input
}

Textarea is textarea type

func (*Textarea) Render

func (t *Textarea) Render(f *Field, cs []Choice, vs []string) template.HTML

Render returns string with rendered textarea

type Type

type Type interface {
	// Tells if fields type should accept multiple values
	IsMultiValue() bool
	// Cleans data before it goes to user
	CleanData(values []string) interface{}
	// Render form
	Render(*Field, []Choice, []string) template.HTML
}

Type is interface that tells us

type Validator

type Validator interface {
	IsValid(values []string) (bool, []string)
}

Validator is interface for all validators

Jump to

Keyboard shortcuts

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