diff --git a/controller/form.go b/controller/form.go new file mode 100644 index 0000000..dbf9078 --- /dev/null +++ b/controller/form.go @@ -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) + } +} diff --git a/routes/contact.go b/routes/contact.go index a787e3f..343c8da 100644 --- a/routes/contact.go +++ b/routes/contact.go @@ -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, "HELLO!") } else { msg.Success(ctx, "Thank you for contacting us!") diff --git a/templates/components/forms.gohtml b/templates/components/forms.gohtml index 3a6d15e..fba4442 100644 --- a/templates/components/forms.gohtml +++ b/templates/components/forms.gohtml @@ -1,3 +1,9 @@ {{define "csrf"}} +{{end}} + +{{define "form-field-errors"}} + {{range .}} +

{{.}}

+ {{end}} {{end}} \ No newline at end of file diff --git a/templates/layouts/main.gohtml b/templates/layouts/main.gohtml index 8b73007..c153ce8 100644 --- a/templates/layouts/main.gohtml +++ b/templates/layouts/main.gohtml @@ -1,11 +1,11 @@ - + {{template "metatags" .}} {{template "css" .}} {{template "js" .}} - +