Add autocomplete attribute to forms
This commit is contained in:
parent
cf59bd7db7
commit
c6f77e86fe
9 changed files with 169 additions and 72 deletions
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed "templates/password.html"
|
//go:embed "templates/password.html"
|
||||||
|
|
@ -28,13 +29,13 @@ func (panel *UserPanel) routePassword(ctx context.Context) http.Handler {
|
||||||
passwordTemplate := panel.Dependencies.Custom.Template(passwordTemplate)
|
passwordTemplate := panel.Dependencies.Custom.Template(passwordTemplate)
|
||||||
|
|
||||||
return &httpx.Form[struct{}]{
|
return &httpx.Form[struct{}]{
|
||||||
Fields: []httpx.Field{
|
Fields: []field.Field{
|
||||||
{Name: "old", Type: httpx.PasswordField, EmptyOnError: true, Label: "Current Password"},
|
{Name: "old", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||||
{Name: "otp", Type: httpx.TextField, EmptyOnError: true, Label: "Current Passcode (optional)"},
|
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Current Passcode (optional)"},
|
||||||
{Name: "new", Type: httpx.PasswordField, EmptyOnError: true, Label: "New Password"},
|
{Name: "new", Type: field.Password, Autocomplete: field.NewPassword, EmptyOnError: true, Label: "New Password"},
|
||||||
{Name: "new2", Type: httpx.PasswordField, EmptyOnError: true, Label: "New Password (again)"},
|
{Name: "new2", Type: field.Password, Autocomplete: field.NewPassword, EmptyOnError: true, Label: "New Password (again)"},
|
||||||
},
|
},
|
||||||
FieldTemplate: httpx.PureCSSFieldTemplate,
|
FieldTemplate: field.PureCSSFieldTemplate,
|
||||||
|
|
||||||
RenderTemplate: passwordTemplate,
|
RenderTemplate: passwordTemplate,
|
||||||
RenderTemplateContext: panel.UserFormContext,
|
RenderTemplateContext: panel.UserFormContext,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
@ -20,10 +21,10 @@ func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler {
|
||||||
totpEnableTemplate := panel.Dependencies.Custom.Template(totpEnableTemplate)
|
totpEnableTemplate := panel.Dependencies.Custom.Template(totpEnableTemplate)
|
||||||
|
|
||||||
return &httpx.Form[struct{}]{
|
return &httpx.Form[struct{}]{
|
||||||
Fields: []httpx.Field{
|
Fields: []field.Field{
|
||||||
{Name: "password", Type: httpx.PasswordField, EmptyOnError: true, Label: "Current Password"},
|
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||||
},
|
},
|
||||||
FieldTemplate: httpx.PureCSSFieldTemplate,
|
FieldTemplate: field.PureCSSFieldTemplate,
|
||||||
|
|
||||||
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
||||||
user, err := panel.Dependencies.Auth.UserOf(r)
|
user, err := panel.Dependencies.Auth.UserOf(r)
|
||||||
|
|
@ -78,11 +79,11 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
||||||
totpEnrollTemplate := panel.Dependencies.Custom.Template(totpEnrollTemplate)
|
totpEnrollTemplate := panel.Dependencies.Custom.Template(totpEnrollTemplate)
|
||||||
|
|
||||||
return &httpx.Form[struct{}]{
|
return &httpx.Form[struct{}]{
|
||||||
Fields: []httpx.Field{
|
Fields: []field.Field{
|
||||||
{Name: "password", Type: httpx.PasswordField, EmptyOnError: true, Label: "Current Password"},
|
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||||
{Name: "otp", Type: httpx.TextField, EmptyOnError: true, Label: "Passcode"},
|
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Passcode"},
|
||||||
},
|
},
|
||||||
FieldTemplate: httpx.PureCSSFieldTemplate,
|
FieldTemplate: field.PureCSSFieldTemplate,
|
||||||
|
|
||||||
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
||||||
user, err := panel.Dependencies.Auth.UserOf(r)
|
user, err := panel.Dependencies.Auth.UserOf(r)
|
||||||
|
|
@ -152,11 +153,11 @@ func (panel *UserPanel) routeTOTPDisable(ctx context.Context) http.Handler {
|
||||||
totpDisableTemplate := panel.Dependencies.Custom.Template(totpDisableTemplate)
|
totpDisableTemplate := panel.Dependencies.Custom.Template(totpDisableTemplate)
|
||||||
|
|
||||||
return &httpx.Form[struct{}]{
|
return &httpx.Form[struct{}]{
|
||||||
Fields: []httpx.Field{
|
Fields: []field.Field{
|
||||||
{Name: "password", Type: httpx.PasswordField, EmptyOnError: true, Label: "Current Password"},
|
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Current Password"},
|
||||||
{Name: "otp", Type: httpx.TextField, EmptyOnError: true, Label: "Current Passcode"},
|
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Current Passcode"},
|
||||||
},
|
},
|
||||||
FieldTemplate: httpx.PureCSSFieldTemplate,
|
FieldTemplate: field.PureCSSFieldTemplate,
|
||||||
|
|
||||||
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
SkipForm: func(r *http.Request) (data struct{}, skip bool) {
|
||||||
user, err := panel.Dependencies.Auth.UserOf(r)
|
user, err := panel.Dependencies.Auth.UserOf(r)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
@ -114,12 +115,12 @@ func (auth *Auth) authLogin(ctx context.Context) http.Handler {
|
||||||
loginTemplate := auth.Dependencies.Custom.Template(loginTemplate)
|
loginTemplate := auth.Dependencies.Custom.Template(loginTemplate)
|
||||||
|
|
||||||
return &httpx.Form[*AuthUser]{
|
return &httpx.Form[*AuthUser]{
|
||||||
Fields: []httpx.Field{
|
Fields: []field.Field{
|
||||||
{Name: "username", Type: httpx.TextField, Label: "Username"},
|
{Name: "username", Type: field.Text, Autocomplete: field.Username, Label: "Username"},
|
||||||
{Name: "password", Type: httpx.PasswordField, EmptyOnError: true, Label: "Password"},
|
{Name: "password", Type: field.Password, Autocomplete: field.CurrentPassword, EmptyOnError: true, Label: "Password"},
|
||||||
{Name: "otp", Type: httpx.TextField, EmptyOnError: true, Label: "Passcode (optional)"},
|
{Name: "otp", Type: field.Text, Autocomplete: field.OneTimeCode, EmptyOnError: true, Label: "Passcode (optional)"},
|
||||||
},
|
},
|
||||||
FieldTemplate: httpx.PureCSSFieldTemplate,
|
FieldTemplate: field.PureCSSFieldTemplate,
|
||||||
|
|
||||||
RenderForm: func(context httpx.FormContext, w http.ResponseWriter, r *http.Request) {
|
RenderForm: func(context httpx.FormContext, w http.ResponseWriter, r *http.Request) {
|
||||||
if context.Err != nil {
|
if context.Err != nil {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
"github.com/FAU-CDI/wisski-distillery/internal/models"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
|
@ -116,7 +117,7 @@ func (admin *Admin) postGrants(r *http.Request) (gc grantsContext, err error) {
|
||||||
delete = r.PostFormValue("action") == "delete"
|
delete = r.PostFormValue("action") == "delete"
|
||||||
distilleryUser = r.PostFormValue("distillery-user")
|
distilleryUser = r.PostFormValue("distillery-user")
|
||||||
drupalUser = r.PostFormValue("drupal-user")
|
drupalUser = r.PostFormValue("drupal-user")
|
||||||
adminRole = r.PostFormValue("admin") == httpx.CheckboxChecked
|
adminRole = r.PostFormValue("admin") == field.CheckboxChecked
|
||||||
)
|
)
|
||||||
|
|
||||||
// set the common fields
|
// set the common fields
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -57,18 +58,18 @@ func (admin *Admin) createUser(ctx context.Context) http.Handler {
|
||||||
userCreateTemplate := admin.Dependencies.Custom.Template(userCreateTemplate)
|
userCreateTemplate := admin.Dependencies.Custom.Template(userCreateTemplate)
|
||||||
|
|
||||||
return &httpx.Form[createUserResult]{
|
return &httpx.Form[createUserResult]{
|
||||||
Fields: []httpx.Field{
|
Fields: []field.Field{
|
||||||
{Name: "username", Type: httpx.TextField, Label: "Username"},
|
{Name: "username", Type: field.Text, Autocomplete: field.Username, Label: "Username"},
|
||||||
{Name: "password", Type: httpx.PasswordField, Label: "Password"},
|
{Name: "password", Type: field.Password, Autocomplete: field.NewPassword, Label: "Password"},
|
||||||
{Name: "admin", Type: httpx.CheckboxField, Label: "Distillery Administrator"},
|
{Name: "admin", Type: field.Checkbox, Label: "Distillery Administrator"},
|
||||||
},
|
},
|
||||||
FieldTemplate: httpx.PureCSSFieldTemplate,
|
FieldTemplate: field.PureCSSFieldTemplate,
|
||||||
|
|
||||||
RenderTemplate: userCreateTemplate,
|
RenderTemplate: userCreateTemplate,
|
||||||
RenderTemplateContext: admin.Dependencies.Custom.RenderContext,
|
RenderTemplateContext: admin.Dependencies.Custom.RenderContext,
|
||||||
|
|
||||||
Validate: func(r *http.Request, values map[string]string) (cu createUserResult, err error) {
|
Validate: func(r *http.Request, values map[string]string) (cu createUserResult, err error) {
|
||||||
cu.User, cu.Passsword, cu.Admin = values["username"], values["password"], values["admin"] == httpx.CheckboxChecked
|
cu.User, cu.Passsword, cu.Admin = values["username"], values["password"], values["admin"] == field.CheckboxChecked
|
||||||
|
|
||||||
if cu.User == "" {
|
if cu.User == "" {
|
||||||
return cu, errCreateInvalidUsername
|
return cu, errCreateInvalidUsername
|
||||||
|
|
|
||||||
63
pkg/httpx/field/autocomplete.go
Normal file
63
pkg/httpx/field/autocomplete.go
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
package field
|
||||||
|
|
||||||
|
// Autocomplete represents different autocomplete options
|
||||||
|
type Autocomplete string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Off Autocomplete = "off"
|
||||||
|
On Autocomplete = "on"
|
||||||
|
Name Autocomplete = "name"
|
||||||
|
HonorificPrefix Autocomplete = "honorific-prefix"
|
||||||
|
GivenName Autocomplete = "given-name"
|
||||||
|
AdditionalName Autocomplete = "additional-name"
|
||||||
|
FamilyName Autocomplete = "family-name"
|
||||||
|
HonorificSuffix Autocomplete = "honorific-suffix"
|
||||||
|
Nickname Autocomplete = "nickname"
|
||||||
|
Email_ Autocomplete = "email"
|
||||||
|
Username Autocomplete = "username"
|
||||||
|
NewPassword Autocomplete = "new-password"
|
||||||
|
CurrentPassword Autocomplete = "current-password"
|
||||||
|
OneTimeCode Autocomplete = "one-time-code"
|
||||||
|
OrganizationTitle Autocomplete = "organization-title"
|
||||||
|
Organization Autocomplete = "organization"
|
||||||
|
AddressLine1 Autocomplete = "address-line1"
|
||||||
|
AddressLine2 Autocomplete = "address-line2"
|
||||||
|
AddressLine3 Autocomplete = "address-line3"
|
||||||
|
StreetAddress Autocomplete = "street-address"
|
||||||
|
AddressLevel4 Autocomplete = "address-level4"
|
||||||
|
AddressLevel3 Autocomplete = "address-level3"
|
||||||
|
AddressLevel2 Autocomplete = "address-level2"
|
||||||
|
AddressLevel1 Autocomplete = "address-level1"
|
||||||
|
Country Autocomplete = "country"
|
||||||
|
CountryName Autocomplete = "country-name"
|
||||||
|
PostalCode Autocomplete = "postal-code"
|
||||||
|
CcName Autocomplete = "cc-name"
|
||||||
|
CcGivenName Autocomplete = "cc-given-name"
|
||||||
|
CcAdditionalName Autocomplete = "cc-additional-name"
|
||||||
|
CcFamilyName Autocomplete = "cc-family-name"
|
||||||
|
CcNumber Autocomplete = "cc-number"
|
||||||
|
CcExp Autocomplete = "cc-exp"
|
||||||
|
CcExpMonth Autocomplete = "cc-exp-month"
|
||||||
|
CcExpYear Autocomplete = "cc-exp-year"
|
||||||
|
CcCsc Autocomplete = "cc-csc"
|
||||||
|
CcType Autocomplete = "cc-type"
|
||||||
|
TransactionCurrency Autocomplete = "transaction-currency"
|
||||||
|
TransactionAmount Autocomplete = "transaction-amount"
|
||||||
|
Language Autocomplete = "language"
|
||||||
|
Bday Autocomplete = "bday"
|
||||||
|
BdayDay Autocomplete = "bday-day"
|
||||||
|
BdayMonth Autocomplete = "bday-month"
|
||||||
|
BdayYear Autocomplete = "bday-year"
|
||||||
|
Sex Autocomplete = "sex"
|
||||||
|
Tel_ Autocomplete = "tel"
|
||||||
|
TelCountryCode Autocomplete = "tel-country-code"
|
||||||
|
TelNational Autocomplete = "tel-national"
|
||||||
|
TelAreaCode Autocomplete = "tel-area-code"
|
||||||
|
TelLocal Autocomplete = "tel-local"
|
||||||
|
TelLocalPrefix Autocomplete = "tel-local-prefix"
|
||||||
|
TelLocalSuffix Autocomplete = "tel-local-suffix"
|
||||||
|
TelExtension Autocomplete = "tel-extension"
|
||||||
|
Impp Autocomplete = "impp"
|
||||||
|
Url_ Autocomplete = "url"
|
||||||
|
Photo Autocomplete = "photo"
|
||||||
|
)
|
||||||
40
pkg/httpx/field/field.go
Normal file
40
pkg/httpx/field/field.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package field
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultFieldTemplate is the default template to render fields.
|
||||||
|
var DefaultFieldTemplate = template.Must(template.New("").Parse(`<input type="{{.Type}}" value="{{.Value}}" name="{{.Name}}" placeholder={{.Placeholder}}{{if .Autocomplete }} autocomplete="{{.Autocomplete}}{{end}}>`))
|
||||||
|
var PureCSSFieldTemplate = template.Must(template.New("").Parse(`
|
||||||
|
<div class="pure-control-group"><label for="{{.Name}}">{{.Label}}</label><input type="{{.Type}}" value="{{.Value}}" name="{{.Name}}" id="{{.Name}}" placeholder="{{.Placeholder}}"{{if .Autocomplete }} autocomplete="{{.Autocomplete}}" {{end}}></div>`))
|
||||||
|
|
||||||
|
// Field represents a field inside a form.
|
||||||
|
type Field struct {
|
||||||
|
Name string // Name is the name of the field
|
||||||
|
Type InputType // Type is the type of the field. It corresponds to the "name" attribute in html.
|
||||||
|
|
||||||
|
Placeholder string // Value for the "placeholder" attribute
|
||||||
|
Label string // (External) Label for the field. Not used by the default template.
|
||||||
|
|
||||||
|
Autocomplete Autocomplete
|
||||||
|
|
||||||
|
EmptyOnError bool // indicates if the field should be reset on error
|
||||||
|
}
|
||||||
|
|
||||||
|
// fieldContext is passed to the template context
|
||||||
|
type fieldContext struct {
|
||||||
|
Field
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (field Field) WriteTo(w io.Writer, template *template.Template, value string) {
|
||||||
|
if template == nil {
|
||||||
|
template = DefaultFieldTemplate
|
||||||
|
}
|
||||||
|
template.Execute(w, fieldContext{Field: field, Value: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckboxChecked is the default value of a checked checkbox
|
||||||
|
const CheckboxChecked = "on"
|
||||||
30
pkg/httpx/field/type.go
Normal file
30
pkg/httpx/field/type.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
package field
|
||||||
|
|
||||||
|
// InputType represents the type of input
|
||||||
|
type InputType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Button InputType = "button"
|
||||||
|
Checkbox InputType = "checkbox"
|
||||||
|
Color InputType = "color"
|
||||||
|
Date InputType = "date"
|
||||||
|
DatetimeLocal InputType = "datetime-local"
|
||||||
|
Email InputType = "email"
|
||||||
|
File InputType = "file"
|
||||||
|
Hidden InputType = "hidden"
|
||||||
|
Image InputType = "image"
|
||||||
|
Month InputType = "month"
|
||||||
|
Number InputType = "number"
|
||||||
|
Password InputType = "password"
|
||||||
|
Radio InputType = "radio"
|
||||||
|
Range InputType = "range"
|
||||||
|
Reset InputType = "reset"
|
||||||
|
Search InputType = "search"
|
||||||
|
Submit InputType = "submit"
|
||||||
|
Tel InputType = "tel"
|
||||||
|
Text InputType = "text"
|
||||||
|
Time InputType = "time"
|
||||||
|
Url InputType = "url"
|
||||||
|
Week InputType = "week"
|
||||||
|
Datetime InputType = "datetime"
|
||||||
|
)
|
||||||
|
|
@ -2,23 +2,18 @@ package httpx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultFieldTemplate is the default template to render fields.
|
|
||||||
var DefaultFieldTemplate = template.Must(template.New("").Parse(`<input type="{{.Type}}" value="{{.Value}}" name="{{.Name}}" placeholder={{.Placeholder}}>`))
|
|
||||||
var PureCSSFieldTemplate = template.Must(template.New("").Parse(`
|
|
||||||
<div class="pure-control-group"><label for="{{.Name}}">{{.Label}}</label><input type="{{.Type}}" value="{{.Value}}" name="{{.Name}}" id="{{.Name}}" placeholder="{{.Placeholder}}"></div>`))
|
|
||||||
|
|
||||||
// Form provides a form that a user can submit via a http POST method call.
|
// Form provides a form that a user can submit via a http POST method call.
|
||||||
// It implements [http.Handler].
|
// It implements [http.Handler].
|
||||||
type Form[D any] struct {
|
type Form[D any] struct {
|
||||||
// Fields are the fields this form consists of.
|
// Fields are the fields this form consists of.
|
||||||
Fields []Field
|
Fields []field.Field
|
||||||
|
|
||||||
// FieldTemplate is an optional template to be executed for each field.
|
// FieldTemplate is an optional template to be executed for each field.
|
||||||
// FieldTemplate may be nil; in which case [DefaultFieldTemplate] is used.
|
// FieldTemplate may be nil; in which case [DefaultFieldTemplate] is used.
|
||||||
|
|
@ -178,39 +173,3 @@ func (form *Form[D]) renderSuccess(data D, values map[string]string, w http.Resp
|
||||||
}
|
}
|
||||||
form.renderForm(err, values, w, r)
|
form.renderForm(err, values, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field represents a field inside a form.
|
|
||||||
type Field struct {
|
|
||||||
Name string // Name is the name of the field
|
|
||||||
Type InputType // Type is the type of the field. It corresponds to the "name" attribute in html.
|
|
||||||
|
|
||||||
Placeholder string // Value for the "placeholder" attribute
|
|
||||||
Label string // (External) Label for the field. Not used by the default template.
|
|
||||||
|
|
||||||
EmptyOnError bool // indicates if the field should be reset on error
|
|
||||||
}
|
|
||||||
|
|
||||||
// fieldContext is passed to the template context
|
|
||||||
type fieldContext struct {
|
|
||||||
Field
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (field Field) WriteTo(w io.Writer, template *template.Template, value string) {
|
|
||||||
if template == nil {
|
|
||||||
template = DefaultFieldTemplate
|
|
||||||
}
|
|
||||||
template.Execute(w, fieldContext{Field: field, Value: value})
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputType represents the type of input
|
|
||||||
type InputType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
TextField InputType = "text"
|
|
||||||
PasswordField InputType = "password"
|
|
||||||
CheckboxField InputType = "checkbox"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CheckboxChecked is the default value of a checked checkbox
|
|
||||||
const CheckboxChecked = "on"
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue