feat: inline validation
This commit is contained in:
parent
14245a02f5
commit
c22ab0a793
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module examples
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/a-h/pathvars v0.0.12
|
||||||
github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5
|
github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5
|
||||||
github.com/rs/xid v1.5.0
|
github.com/rs/xid v1.5.0
|
||||||
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4
|
github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4
|
||||||
|
|
2
go.sum
2
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 h1:EnZqZmtV0YICqWG6MtLNmTcWuFkl2ImyQ63SIpWaM2Y=
|
||||||
github.com/a-h/templ v0.2.233/go.mod h1:h1DdzFMWVApvTcZBNmM6+mD6EPq6uYkncMNF7zdLj9I=
|
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=
|
github.com/a-h/templ v0.2.234-0.20230416205859-20293271f3c5 h1:NeF/iw7KU9W7CYYJimd5x7ooOXCLrfo8FcHdFPUU+2w=
|
||||||
|
|
|
@ -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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
7
main.go
7
main.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"examples/clicktoload"
|
"examples/clicktoload"
|
||||||
"examples/deleterow"
|
"examples/deleterow"
|
||||||
"examples/editrow"
|
"examples/editrow"
|
||||||
|
"examples/inlinevalidation"
|
||||||
"examples/lazyload"
|
"examples/lazyload"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -72,4 +73,10 @@ var examples = []Example{
|
||||||
Slug: "lazy-loading",
|
Slug: "lazy-loading",
|
||||||
Handlers: lazyload.Handlers,
|
Handlers: lazyload.Handlers,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "Inline Validation",
|
||||||
|
Desc: "Demonstrates how to do inline field validation",
|
||||||
|
Slug: "inline-validation",
|
||||||
|
Handlers: inlinevalidation.Handlers,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue