From c22ab0a793ebc609ca85e2bce73aef20f796002f Mon Sep 17 00:00:00 2001 From: joerdav Date: Thu, 27 Apr 2023 10:17:22 +0100 Subject: [PATCH] feat: inline validation --- go.mod | 1 + go.sum | 2 + inlinevalidation/handlers.go | 78 ++++++ inlinevalidation/templates.templ | 36 +++ inlinevalidation/templates_templ.go | 381 ++++++++++++++++++++++++++++ main.go | 7 + 6 files changed, 505 insertions(+) create mode 100644 inlinevalidation/handlers.go create mode 100644 inlinevalidation/templates.templ create mode 100644 inlinevalidation/templates_templ.go diff --git a/go.mod b/go.mod index 851cafb..c70bbe1 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module examples go 1.21 require ( + github.com/a-h/pathvars v0.0.12 github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5 github.com/rs/xid v1.5.0 github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 diff --git a/go.sum b/go.sum index cad4968..fb3442e 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/a-h/pathvars v0.0.12 h1:B4JaZGvHKNgNNlw8LMayPM/Hc0f3xZ2PXivu8YIl/X0= +github.com/a-h/pathvars v0.0.12/go.mod h1:7rLTtvDVyKneR/N65hC0lh2sZ2KRyAmWFaOvv00uxb0= github.com/a-h/templ v0.2.233 h1:EnZqZmtV0YICqWG6MtLNmTcWuFkl2ImyQ63SIpWaM2Y= github.com/a-h/templ v0.2.233/go.mod h1:h1DdzFMWVApvTcZBNmM6+mD6EPq6uYkncMNF7zdLj9I= github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5 h1:NeF/iw7KU9W7CYYJimd5x7ooOXCLrfo8FcHdFPUU+2w= diff --git a/inlinevalidation/handlers.go b/inlinevalidation/handlers.go new file mode 100644 index 0000000..ad6b88e --- /dev/null +++ b/inlinevalidation/handlers.go @@ -0,0 +1,78 @@ +package inlinevalidation + +import ( + "errors" + "net/http" + + "github.com/a-h/pathvars" +) + +var validateMatcher = pathvars.NewExtractor("/inline-validation/validate/{name}") + +func Handlers(prefix string, mux *http.ServeMux) { + mux.HandleFunc(prefix+"/", index) + mux.HandleFunc(prefix+"/validate/", validate) +} + +func index(w http.ResponseWriter, r *http.Request) { + Index().Render(r.Context(), w) +} + +func validate(w http.ResponseWriter, r *http.Request) { + vals, ok := validateMatcher.Extract(r.URL) + if !ok { + w.WriteHeader(500) + return + } + name, ok := vals["name"] + if !ok { + w.WriteHeader(500) + return + } + f, ok := fields[name] + if !ok { + w.WriteHeader(500) + return + } + if err := r.ParseForm(); err != nil { + w.WriteHeader(500) + return + } + val := r.FormValue(name) + inp(f, name, val, f.validator(val)).Render(r.Context(), w) +} + +type field struct { + text string + validator func(string) error +} + +var fields = map[string]field{ + "email": { + text: "Email", + validator: func(value string) error { + if value != "test@test.com" { + return errors.New("Only test@test.com is valid.") + } + return nil + }, + }, + "firstName": { + text: "First Name", + validator: func(value string) error { + if value == "" { + return errors.New("Required") + } + return nil + }, + }, + "lastName": { + text: "Last Name", + validator: func(value string) error { + if value == "" { + return errors.New("Required") + } + return nil + }, + }, +} diff --git a/inlinevalidation/templates.templ b/inlinevalidation/templates.templ new file mode 100644 index 0000000..c25dd6d --- /dev/null +++ b/inlinevalidation/templates.templ @@ -0,0 +1,36 @@ +package inlinevalidation + +import "examples/shared" + +templ demo() { +
+ @inp(fields["email"], "email", "", nil) + @inp(fields["firstName"], "firstName", "", nil) + @inp(fields["lastName"], "lastName", "", nil) +
+
+
+
+} + +templ inp(f field, name, value string, validation error) { +
+ +
+ if validation != nil { +

{ validation.Error() }

+ } +
+} + +templ Index() { + @shared.Layout("Inline Validation") { +

Inline Validation

+ @demo() + } +} + diff --git a/inlinevalidation/templates_templ.go b/inlinevalidation/templates_templ.go new file mode 100644 index 0000000..09d750c --- /dev/null +++ b/inlinevalidation/templates_templ.go @@ -0,0 +1,381 @@ +// Code generated by templ@(devel) DO NOT EDIT. + +package inlinevalidation + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import "context" +import "io" +import "bytes" + +// GoExpression +import "examples/shared" + +func demo() templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_1 := templ.GetChildren(ctx) + if var_1 == nil { + var_1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // TemplElement + err = inp(fields["email"], "email", "", nil).Render(ctx, templBuffer) + if err != nil { + return err + } + // TemplElement + err = inp(fields["firstName"], "firstName", "", nil).Render(ctx, templBuffer) + if err != nil { + return err + } + // TemplElement + err = inp(fields["lastName"], "lastName", "", nil).Render(ctx, templBuffer) + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Text + var_2 := `Submit` + _, err = templBuffer.WriteString(var_2) + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + +func inp(f field, name, value string, validation error) templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_3 := templ.GetChildren(ctx) + if var_3 == nil { + var_3 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // StringExpression + var var_4 string = f.text + _, err = templBuffer.WriteString(templ.EscapeString(var_4)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // If + if validation != nil { + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // StringExpression + var var_5 string = validation.Error() + _, err = templBuffer.WriteString(templ.EscapeString(var_5)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("

") + if err != nil { + return err + } + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + +func Index() templ.Component { + return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + ctx = templ.InitializeContext(ctx) + var_6 := templ.GetChildren(ctx) + if var_6 == nil { + var_6 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + // TemplElement + var_7 := templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) { + templBuffer, templIsBuffer := w.(*bytes.Buffer) + if !templIsBuffer { + templBuffer = templ.GetBuffer() + defer templ.ReleaseBuffer(templBuffer) + } + // Element (standard) + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Text + var_8 := `Inline Validation` + _, err = templBuffer.WriteString(var_8) + if err != nil { + return err + } + _, err = templBuffer.WriteString("") + if err != nil { + return err + } + // Whitespace (normalised) + _, err = templBuffer.WriteString(` `) + if err != nil { + return err + } + // TemplElement + err = demo().Render(ctx, templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) + err = shared.Layout("Inline Validation").Render(templ.WithChildren(ctx, var_7), templBuffer) + if err != nil { + return err + } + if !templIsBuffer { + _, err = io.Copy(w, templBuffer) + } + return err + }) +} + diff --git a/main.go b/main.go index 1b58493..cba1c90 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "examples/clicktoload" "examples/deleterow" "examples/editrow" + "examples/inlinevalidation" "examples/lazyload" "log" "net/http" @@ -72,4 +73,10 @@ var examples = []Example{ Slug: "lazy-loading", Handlers: lazyload.Handlers, }, + { + Name: "Inline Validation", + Desc: "Demonstrates how to do inline field validation", + Slug: "inline-validation", + Handlers: inlinevalidation.Handlers, + }, }