Initial commit of form submission struct.

This commit is contained in:
mikestefanello 2021-12-22 23:40:08 -05:00
parent b61077dac9
commit 57159c4fba
5 changed files with 126 additions and 21 deletions

91
controller/form.go Normal file
View File

@ -0,0 +1,91 @@
package controller
import (
"reflect"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
)
type FormSubmission struct {
IsSubmitted bool
Errors map[string][]string
}
func (f *FormSubmission) Process(ctx echo.Context, form interface{}) error {
f.Errors = make(map[string][]string)
f.IsSubmitted = true
// Validate the form
if err := ctx.Validate(form); err != nil {
f.setErrorMessages(form, err)
}
return nil
}
func (f FormSubmission) HasErrors() bool {
if f.Errors == nil {
return false
}
return len(f.Errors) > 0
}
func (f FormSubmission) FieldHasError(fieldName string) bool {
return len(f.GetFieldErrors(fieldName)) > 0
}
func (f FormSubmission) GetFieldErrors(fieldName string) []string {
if f.Errors == nil {
return []string{}
}
errors, has := f.Errors[fieldName]
if !has {
return []string{}
}
return errors
}
func (f *FormSubmission) setErrorMessages(form interface{}, err error) {
// Only this is supported right now
ves, ok := err.(validator.ValidationErrors)
if !ok {
return
}
formType := reflect.TypeOf(form)
for _, ve := range ves {
var message string
// Default the field form name to the name of the struct field
fieldName := ve.StructField()
// Attempt to get the form field name from the field's struct tag
if field, ok := formType.FieldByName(ve.Field()); ok {
if fieldNameTag := field.Tag.Get("form"); fieldNameTag != "" {
fieldName = fieldNameTag
}
}
// Provide better error messages depending on the failed validation tag
// This should be expanded as you use additional tags in your validation
switch ve.Tag() {
case "required":
message = "This field is required."
case "email":
message = "Enter a valid email address."
case "eqfield":
message = "Does not match."
default:
message = "Invalid value."
}
// Add the error
f.Errors[fieldName] = append(f.Errors[fieldName], message)
}
}

View File

@ -16,8 +16,9 @@ type (
}
ContactForm struct {
Email string `form:"email" validate:"required,email" label:"Email address"`
Message string `form:"message" validate:"required" label:"Message"`
Email string `form:"email" validate:"required,email"`
Message string `form:"message" validate:"required"`
Submission controller.FormSubmission
}
)
@ -36,28 +37,31 @@ func (c *Contact) Get(ctx echo.Context) error {
}
func (c *Contact) Post(ctx echo.Context) error {
fail := func(message string, err error) error {
ctx.Logger().Errorf("%s: %v", message, err)
msg.Danger(ctx, "An error occurred. Please try again.")
return c.Get(ctx)
}
//fail := func(message string, err error) error {
// ctx.Logger().Errorf("%s: %v", message, err)
// msg.Danger(ctx, "An error occurred. Please try again.")
// return c.Get(ctx)
//}
// Parse the form values
var form ContactForm
if err := ctx.Bind(&form); err != nil {
return fail("unable to parse contact form", err)
ctx.Logger().Error(err)
}
if err := form.Submission.Process(ctx, form); err != nil {
// TOOD
}
ctx.Set(context.FormKey, form)
// Validate the form
if err := ctx.Validate(form); err != nil {
c.SetValidationErrorMessages(ctx, err, form)
if form.Submission.HasErrors() {
return c.Get(ctx)
}
p := controller.NewHTMX(ctx)
htmx := controller.NewHTMXRequest(ctx)
if p.Request.Enabled {
if htmx.Enabled {
return ctx.String(http.StatusOK, "<b>HELLO!</b>")
} else {
msg.Success(ctx, "Thank you for contacting us!")

View File

@ -1,3 +1,9 @@
{{define "csrf"}}
<input type="hidden" name="csrf" value="{{.CSRF}}"/>
{{end}}
{{define "form-field-errors"}}
{{range .}}
<p class="help is-danger">{{.}}</p>
{{end}}
{{end}}

View File

@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" style="height:100%;">
<head>
{{template "metatags" .}}
{{template "css" .}}
{{template "js" .}}
</head>
<body>
<body class="has-background-light" style="min-height:100%;">
<nav class="navbar is-dark">
<div class="container" hx-boost="true">
<div class="navbar-brand">
@ -28,12 +28,14 @@
<section class="section">
<div class="container">
{{- if .Title}}
<h1 class="title">{{.Title}}</h1>
{{- end}}
<div class="box">
{{- if .Title}}
<h1 class="title">{{.Title}}</h1>
{{- end}}
{{template "messages" .}}
{{template "content" .}}
{{template "messages" .}}
{{template "content" .}}
</div>
</div>
</section>
</body>

View File

@ -1,10 +1,11 @@
{{define "content"}}
<form id="contact" method="post" hx-post>
<form id="contact" method="post" >
<div class="field">
<label for="email" class="label">Email address</label>
<div class="control">
<input id="email" name="email" type="email" class="input" value="{{.Data.Email}}">
</div>
{{template "form-field-errors" (.Data.Submission.GetFieldErrors "email")}}
</div>
<div class="field">
@ -12,6 +13,7 @@
<div class="control">
<textarea id="message" name="message" class="textarea">{{.Data.Message}}</textarea>
</div>
{{template "form-field-errors" (.Data.Submission.GetFieldErrors "message")}}
</div>
<div class="field is-grouped">