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() { + <form hx-post="/inline-validation/contact"> + @inp(fields["email"], "email", "", nil) + @inp(fields["firstName"], "firstName", "", nil) + @inp(fields["lastName"], "lastName", "", nil) + <div class="field"> + <div class="control"><button class="button is-link">Submit</button></div> + </div> + </form> +} + +templ inp(f field, name, value string, validation error) { + <div hx-target="this" hx-swap="outerHTML" class="field"> + <label class="label">{ f.text }</label> + <div class="control"><input class="input" + if validation != nil { + class="is-danger" + } + name={ name } value={ value } hx-post={ "/inline-validation/validate/"+name }/></div> + if validation != nil { + <p class="help is-danger">{ validation.Error() }</p> + } + </div> +} + +templ Index() { + @shared.Layout("Inline Validation") { + <h2 class="title">Inline Validation</h2> + @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("<form") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" hx-post=\"/inline-validation/contact\"") + if err != nil { + return err + } + _, 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("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"field\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<button") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"button is-link\"") + if err != nil { + return err + } + _, 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("</button>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</form>") + 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("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" hx-target=\"this\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-swap=\"outerHTML\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" class=\"field\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<label") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"label\"") + if err != nil { + return err + } + _, 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("</label>") + if err != nil { + return err + } + // Element (standard) + _, err = templBuffer.WriteString("<div") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"control\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + // Element (void) + _, err = templBuffer.WriteString("<input") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"input\"") + if err != nil { + return err + } + if validation != nil { + // Element Attributes + _, err = templBuffer.WriteString(" class=\"is-danger\"") + if err != nil { + return err + } + } + _, err = templBuffer.WriteString(" name=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(templ.EscapeString(name)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" value=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(templ.EscapeString(value)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(" hx-post=") + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(templ.EscapeString("/inline-validation/validate/"+name)) + if err != nil { + return err + } + _, err = templBuffer.WriteString("\"") + if err != nil { + return err + } + _, err = templBuffer.WriteString(">") + if err != nil { + return err + } + _, err = templBuffer.WriteString("</div>") + if err != nil { + return err + } + // If + if validation != nil { + // Element (standard) + _, err = templBuffer.WriteString("<p") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"help is-danger\"") + if err != nil { + return err + } + _, 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("</p>") + if err != nil { + return err + } + } + _, err = templBuffer.WriteString("</div>") + 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("<h2") + if err != nil { + return err + } + // Element Attributes + _, err = templBuffer.WriteString(" class=\"title\"") + if err != nil { + return err + } + _, 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("</h2>") + 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, + }, }