-
-
diff --git a/.hugo_build.lock b/.hugo_build.lock deleted file mode 100644 index e69de29..0000000 diff --git a/404.html b/404.html deleted file mode 100644 index 5e6a065..0000000 --- a/404.html +++ /dev/null @@ -1,39 +0,0 @@ - - - -
-The validation is designed to validate data against constraints.
-import (
- "gitnet.fr/deblan/go-form/validation"
-)Validate the length of an array, a slice or a string
-c := validation.NewLength()
-
-// Define minimum
-c.WithMin(1)
-
-// Define minimum
-c.WithMax(100)
-
-// Define min and max
-// Equivalent to c.WithMin(50).WithMax(50)
-c.WithExact(50)validation.NewMail()validation.NewNotBlank()Validate a number
-c := validation.NewRange()
-
-// Define minimum
-c.WithMin(1)
-
-// Define minimum
-c.WithMax(100)
-
-// Define min and max
-// Equivalent to c.WithMin(1).WithMax(100)
-c.WithRange(1, 100)Validate a string with a regex
-c := validation.NewRegex(`expression`)
-
-// The value must match
-c.MustMatch()
-
-// The value must not match
-c.MustNotMatch()Validate that a number is even.
-validation.NewIsEven()Validate that a number is odd.
-validation.NewIsOdd()Use case: you want to validate that the data equals “example”
-package validation
-
-import (
- "reflect"
-
- v "gitnet.fr/deblan/go-form/validation"
-)
-
-// Define a struct
-type IsExample struct {
- Message string
- TypeErrorMessage string
-}
-
-// Create a factory
-func NewIsExample() IsEven {
- return IsEven{
- Message: "This value does not equal \"example\".",
- TypeErrorMessage: "This value can not be processed.",
- }
-}
-
-// Implement the validation
-func (c IsExample) Validate(data any) []Error {
- errors := []Error{}
-
- // Should not validate blank data
- if len(v.NewNotBlank().Validate(data)) != 0 {
- return []Error{}
- }
-
- t := reflect.TypeOf(data)
-
- if t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
-
- switch t.Kind() {
- case reflect.String:
- if data.(string) != "example" {
- errors = append(errors, Error(c.Message))
- }
-
- default:
- errors = append(errors, Error(c.TypeErrorMessage))
- }
-
- return errors
-}A field represents a field in a form.
-func NewFieldCheckbox(name string) *Fieldfield := form.NewFieldCheckbox("Foo")
-Generates an input[type=checkbox]
-func NewFieldChoice(name string) *Field
-field := form.NewFieldChoice("Foo")
-
-Generates inputs (checkbox or radio) or selects
-func NewFieldCsrf(name string) *Field
-field := form.NewFieldCsrf("Foo")
-
-func NewFieldDate(name string) *Field
-field := form.NewFieldDate("Foo")
-
-Generates an input[type=date] with default transformers
-func NewFieldDatetime(name string) *Field
-field := form.NewFieldDatetime("Foo")
-
-Generates an input[type=datetime] with default transformers
-func NewFieldDatetimeLocal(name string) *Field
-field := form.NewFieldDatetimeLocal("Foo")
-
-Generates an input[type=datetime-local] with default transformers
-func NewFieldHidden(name string) *Field
-field := form.NewFieldHidden("Foo")
-
-Generates an input[type=hidden]
-func NewFieldMail(name string) *Field
-field := form.NewFieldMail("Foo")
-
-Generates an input[type=email]
-func NewFieldNumber(name string) *Field
-field := form.NewFieldNumber("Foo")
-
-Generates an input[type=number] with default transformers
-func NewFieldPassword(name string) *Field
-field := form.NewFieldPassword("Foo")
-
-Generates an input[type=password]
-func NewFieldRange(name string) *Field
-field := form.NewFieldRange("Foo")
-
-Generates an input[type=range]
-func NewFieldSubForm(name string) *Field
-field := form.NewFieldSubForm("Foo")
-
-Alias:
-func NewSubForm(name string) *FieldGenerates a sub form
-func NewFieldText(name string) *Field
-field := form.NewFieldText("Foo")
-
-Generates an input[type=text]
-func NewFieldTextarea(name string) *Field
-field := form.NewFieldTextarea("Foo")
-
-Generates a textarea
-func NewFieldTime(name string) *Field
-field := form.NewFieldTime("Foo")
-
-Generates an input[type=time] with default transformers
-func NewSubmit(name string) *Field
-field := form.NewSubmit("Foo")
-
-Generates an input[type=submit]
-func (f *Field) Add(children ...*Field) *FieldAppends children
-func (f *Field) Bind(data map[string]any, key *string) errorBind the data into the given map
-func (f *Field) GetChild(name string) *FieldReturns a child using its name
-func (f *Field) GetId() stringComputes the id of the field
-func (f *Field) GetName() stringComputes the name of the field
-func (f *Field) GetOption(name string) *OptionReturns an option using its name
-func (f *Field) HasChild(name string) boolChecks if the field contains a child using its name
-func (f *Field) HasOption(name string) boolChecks if the field contains an option using its name
-func (f *Field) Mount(data any) errorPopulates the field with data
-func (f *Field) ResetErrors() *FieldResets the field errors
-func (f *Field) WithBeforeBind(callback func(data any) (any, error)) *FieldSets a transformer applied to the data of a field before defining it in a structure
-func (f *Field) WithBeforeMount(callback func(data any) (any, error)) *FieldSets a transformer applied to the structure data before displaying it in a field
-func (f *Field) WithConstraints(constraints ...validation.Constraint) *FieldAppends constraints
-func (f *Field) WithData(data any) *FieldSets data the field
-func (f *Field) WithFixedName() *FieldSets that the name of the field is not computed
-func (f *Field) WithOptions(options ...*Option) *Field| Name | -type | -description | -Info | -
|---|---|---|---|
required |
- bool |
- Add required="true" |
- Does not apply a constraint | -
attr |
- form.Attrs |
- List of extra attributes of the field | -- |
row_attr |
- form.Attrs |
- List of extra attributes of the field’s top container | -- |
label |
- string |
- The label of the field | -Usually show before the field | -
label_attr |
- form.Attrs |
- List of extra attributes of the label | -- |
help |
- string |
- Helper of the field | -- |
help_attr |
- form.Attrs |
- List of extra attributes of the help | -- |
Appends options to the field
-func (f *Field) WithSlice() *FieldSets that the field represents a data slice
- -import (
- "gitnet.fr/deblan/go-form/form"
- "gitnet.fr/deblan/go-form/validation"
-)
-
-type Person struct {
- Name string
- Age int
-}myForm := form.NewForm(
- form.NewFieldText("Name").
- WithConstraints(
- validation.NewNotBlank(),
- ),
- form.NewFieldNumber("Age").
- WithConstraints(
- validation.NewNotBlank(),
- validation.NewRange().WithMin(18),
- ),
-).End()data := Person{}
-
-myForm.Mount(data)
-myForm.IsValid() // false
-
-data = Person{
- Name: "Alice",
- Age: 42,
-}
-
-myForm.Mount(data)
-myForm.IsValid() // trueimport (
- "net/http"
-)
-
-myForm.WithMethod(http.MethodPost)
-
-// req *http.Request
-if req.Method == myForm.Method {
- myForm.HandleRequest(req)
-
- if myForm.IsSubmitted() && myForm.IsValid() {
- myForm.Bind(&data)
- }
-}type Form struct {
- Fields []*Field
- GlobalFields []*Field
- Errors []validation.Error
- Method string
- Action string
- Name string
- Options []*Option
- RequestData *url.Values
-}func NewForm(fields ...*Field) *FormGenerates a new form with default properties
-func (f *Form) Add(fields ...*Field)Appends children
-func (f *Form) AddGlobalField(field *Field)Configures its children deeply
-func (f *Form) Bind(data any) errorCopies datas from the form to a struct
-func (f *Form) End() *FormConfigures its children deeply This function must be called after adding all
-fields
-func (f *Form) GetField(name string) *FieldReturns a child using its name
-func (f *Form) GetOption(name string) *OptionReturns an option using its name
-func (f *Form) HandleRequest(req *http.Request)Processes a request
-func (f *Form) HasField(name string) boolChecks if the form contains a child using its name
-func (f *Form) HasOption(name string) boolChecks if the form contains an option using its name
-func (f *Form) IsSubmitted() boolChecks if the form is submitted
-func (f *Form) IsValid() boolChecks the a form is valid
-func (f *Form) Mount(data any) errorCopies datas from a struct to the form
-func (f *Form) ResetErrors() *FormResets the form errors
-func (f *Form) WithAction(v string) *FormSets the action of the form (eg: “/”)
-func (f *Form) WithMethod(v string) *FormSets the method of the format (http.MethodPost, http.MethodGet, …)
-func (f *Form) WithName(v string) *FormSets the name of the form (used to compute name of fields)
-func (f *Form) WithOptions(options ...*Option) *FormAppends options to the form
-| Name | -Type | -Description | -
|---|---|---|
attr |
- map[string]string |
- List of extra attributes | -
help |
- string |
- Helper | -
Creating and processing HTML forms is hard and repetitive. You need to deal with rendering HTML form fields, validating submitted data, mapping the form data into objects and a lot more. go-form includes a powerful form feature that provides all these features.
-go-form is heavily influenced by Symfony Form. It includes:
-go get gitnet.fr/deblan/go-formgo-form allows you to render a form using Go’s built-in template engine. -Here is a simple example that displays a form:
-myForm := form.NewForm(...)
-
-render := theme.NewRenderer(theme.Bootstrap5)
-
-tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`
- <html>
- <head>
- <title>My form</title>
- </head>
- <body>
- {{ form .Form }}
- </body>
- </html>
-`)
-
-b := new(strings.Builder)
-
-tpl.Execute(w, map[string]any{
- "Form": myForm,
-})
-
-fmt.Println(b.String())
-@import "fmt"
-@import "html/template"
-@import "strings"
-@import
-@import "github.com/yosssi/gohtml"
-@import "gitnet.fr/deblan/go-form/example"
-@import "gitnet.fr/deblan/go-form/theme"
-
-form := example.CreateDataForm()
-render := theme.NewRenderer(theme.Html5)
-// render := theme.NewRenderer(theme.Bootstrap5)
-
-tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`{{ form .Form }}`)
-
-buff := new(strings.Builder)
-
-tpl.Execute(buff, map[string]any{
- "Form": form,
-})
-
-fmt.Println(gohtml.Format(buff.String()))
-
-Other helper functions are available to render specific parts of the form:
-form_errors: displays the form’s global errorsform_row : renders the label, errors, and widget of a fieldform_label: renders only the label of a fieldform_widget: renders only the widget of a fieldform_widget_errors: renders only the errors of a specific fieldgo-form provides 2 themes:
-theme.Html5: a basic view without classestheme.Bootstrap5: a theme for Bootstrap 5You can add a custom theme. Learn by reading the Bootstrap5 theme.
- -import (
- "html/template"
- "net/http"
-
- "gitnet.fr/deblan/go-form/form"
- "gitnet.fr/deblan/go-form/theme"
-)// Let's create a new form
-// You can pass *form.Field as arguments
-myForm := form.NewForm(field1, field2, ...)
-
-// Add somes fields
-myForm.Add(field3, field4, ...)
-
-// Set the method
-// <form method="POST" ...>
-myForm.WithMethod(http.MethodPost)
-
-// Define the action
-// <form action="/" ...>
-myForm.WithAction("/")
-
-// Set a name
-myForm.WithName("myForm")
-
-// Add options
-myForm.WithOptions(option1, option2, ...)
-
-// When all fields are added, call End()
-myForm.End()Some options are natively supported in go-form themes.
-myForm.WithOptions(
- form.NewOption("help", "A help for the form"),
- // <form data-foo="bar" data-bar="bar" ...
- form.NewOption("attr", form.Attrs{
- "data-foo": "foo",
- "data-bar": "bar",
- }),
-)This step is not required when you does not want to pre-fill the form. -Your struct can be complexe and the form only map existing properties.
-type Person struct {
- Name string
- Age int
-}
-
-data := Person{
- Name: "Alice",
- Age: 42,
-}
-
-// Assuming 2 fields named "Name" and "Age" exist
-myForm.Mount(data)http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- // myForm := form.NewForm(...)
-
- render := theme.NewRenderer(theme.Html5)
- tpl, _ := template.New("page").Funcs(render.FuncMap()).Parse(`{{ form .Form }}`)
-
- w.Header().Set("Content-Type", "text/html; charset=utf-8")
-
- tpl.Execute(w, map[string]any{
- "Form": myForm,
- })
-}This is the final step. After the form handles the request, you can check if the form has been submitted, check the values are valid and finally populate your struct.
-http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- // data := Person{...}
- // myForm := form.NewForm(...)
-
- if r.Method == myForm.Method {
- myForm.HandleRequest(r)
-
- if myForm.IsSubmitted() && myForm.IsValid() {
- myForm.Bind(&data)
- }
- }
-}){{ .dump }}
+ {{ .json }}
+ {{ .dump }}
+ {{ .json }}
+ The form is valid!
+ {{else}} +The form is invalid!
+ {{end}} + + {{ form .form }} + + + + + diff --git a/favicon-16x16.png b/favicon-16x16.png deleted file mode 100644 index 0f2dd2b..0000000 Binary files a/favicon-16x16.png and /dev/null differ diff --git a/favicon-32x32.png b/favicon-32x32.png deleted file mode 100644 index 5c1aea5..0000000 Binary files a/favicon-32x32.png and /dev/null differ diff --git a/favicon-dark.svg b/favicon-dark.svg deleted file mode 100644 index 3b49e35..0000000 --- a/favicon-dark.svg +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/favicon.ico b/favicon.ico deleted file mode 100644 index 553fa15..0000000 Binary files a/favicon.ico and /dev/null differ diff --git a/favicon.svg b/favicon.svg deleted file mode 100644 index 6a08d10..0000000 --- a/favicon.svg +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/form/field.go b/form/field.go new file mode 100644 index 0000000..fe63326 --- /dev/null +++ b/form/field.go @@ -0,0 +1,423 @@ +package form + +// @license GNU AGPL version 3 or any later version +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see