init
This commit is contained in:
commit
df9b26c339
8 changed files with 368 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/go-form-demo
|
||||
/vendor
|
||||
12
README.md
Normal file
12
README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Demo of [deblan/go-form](https://gitnet.fr/deblan/go-form)
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
git clone https://gitnet.fr/deblan/go-form-demo
|
||||
go mod tidy
|
||||
go build -o demo
|
||||
./demo
|
||||
```
|
||||
|
||||
Then browse http://127.0.0.1:1324.
|
||||
190
controller/template.go
Normal file
190
controller/template.go
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/yassinebenaid/godump"
|
||||
"gitnet.fr/deblan/go-form-demo/form"
|
||||
"gitnet.fr/deblan/go-form/theme"
|
||||
)
|
||||
|
||||
func UsingTemplate(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
|
||||
var dumped any
|
||||
|
||||
// New instances of datas
|
||||
themeValue := form.NewTheme()
|
||||
contactValue := form.Contact{}
|
||||
|
||||
// Theme form handler
|
||||
themeSelectorForm := form.CreateThemeSelectorForm()
|
||||
themeSelectorForm.HandleRequest(r)
|
||||
if themeSelectorForm.IsSubmitted() && themeSelectorForm.IsValid() {
|
||||
themeSelectorForm.Bind(themeValue)
|
||||
}
|
||||
|
||||
// Contact form handler
|
||||
contactForm := form.CreateContactForm(themeValue.Value == "Bootstrap5")
|
||||
if r.Method == contactForm.Method {
|
||||
contactForm.HandleRequest(r)
|
||||
|
||||
if contactForm.IsSubmitted() && contactForm.IsValid() {
|
||||
contactForm.Bind(&contactValue)
|
||||
|
||||
// For debugging
|
||||
var dump godump.Dumper
|
||||
dump.Theme = godump.Theme{}
|
||||
dumped = dump.Sprint(contactValue)
|
||||
}
|
||||
}
|
||||
|
||||
render := theme.NewRenderer(themes[themeValue.Value])
|
||||
|
||||
tpl, _ := template.New("page").
|
||||
Funcs(render.FuncMap()).
|
||||
Parse(pageTemplates[themeValue.Value])
|
||||
|
||||
tpl.Execute(w, map[string]any{
|
||||
"ContactForm": contactForm,
|
||||
"ThemeSelectorForm": themeSelectorForm,
|
||||
"IsSent": dumped != nil,
|
||||
"Dump": dumped,
|
||||
})
|
||||
}
|
||||
|
||||
var pageTemplates = map[string]string{
|
||||
"Html5": `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Form</title>
|
||||
<style>
|
||||
body {
|
||||
background: #434c64;
|
||||
}
|
||||
|
||||
#theme-form * {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#contact-form > div {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
#contact-form input:not([type=submit]),
|
||||
#contact-form select,
|
||||
#contact-form textarea {
|
||||
margin-bottom: 16px;
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
input:not([type=submit]), select, textarea {
|
||||
padding: 3px 7px;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #333;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.help {
|
||||
color: #999;
|
||||
margin-top: -9px;
|
||||
}
|
||||
|
||||
.required::after {
|
||||
content: "*";
|
||||
color: red;
|
||||
display: inline-block;
|
||||
padding-left: 3px;
|
||||
}
|
||||
|
||||
.page {
|
||||
font-family: Verdana;
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
border: 1px solid #ccc;
|
||||
background: #ffffffee;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
ul, li {
|
||||
color: red;
|
||||
padding: 0;
|
||||
margin: 3px 0;
|
||||
list-style: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
{{ if .IsSent }}
|
||||
<p>
|
||||
<strong>OK!</strong>
|
||||
<pre>{{ .Dump }}</pre>
|
||||
</p>
|
||||
{{ else }}
|
||||
{{ form .ThemeSelectorForm }}
|
||||
|
||||
<h1>Contact form</h1>
|
||||
|
||||
{{ form .ContactForm }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</body>
|
||||
</html>`,
|
||||
"Bootstrap5": `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
|
||||
<title>Form</title>
|
||||
<style>
|
||||
#theme-form * {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#contact-form > div {
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.required::after {
|
||||
content: "*";
|
||||
color: red;
|
||||
display: inline-block;
|
||||
padding-left: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container p-3">
|
||||
{{ if .IsSent }}
|
||||
<div class="alert alert-success my-2">
|
||||
<strong>OK!</strong>
|
||||
<pre>{{ .Dump }}</pre>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{ form .ThemeSelectorForm }}
|
||||
|
||||
<h1 class="mt-3">Contact form</h1>
|
||||
|
||||
{{ form .ContactForm }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</body>
|
||||
</html>`,
|
||||
}
|
||||
|
||||
var themes = map[string]map[string]theme.RenderFunc{
|
||||
"Html5": theme.Html5,
|
||||
"Bootstrap5": theme.Bootstrap5,
|
||||
}
|
||||
82
form/contact.go
Normal file
82
form/contact.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gitnet.fr/deblan/go-form/form"
|
||||
"gitnet.fr/deblan/go-form/validation"
|
||||
)
|
||||
|
||||
type Contact struct {
|
||||
Name string
|
||||
Email string
|
||||
Topic string
|
||||
Message string
|
||||
}
|
||||
|
||||
func CreateContactForm(isBoostrap bool) *form.Form {
|
||||
var rowAttr form.Attrs
|
||||
var btnAttr form.Attrs
|
||||
|
||||
formAttr := form.Attrs{"id": "contact-form"}
|
||||
|
||||
if isBoostrap {
|
||||
rowAttr = form.Attrs{"class": "col-12"}
|
||||
formAttr["class"] = "row"
|
||||
btnAttr = form.Attrs{"class": "btn-primary"}
|
||||
}
|
||||
|
||||
topics := form.NewChoices([]string{"I have a question", "Report a bug"}).
|
||||
WithLabelBuilder(func(key int, item any) string {
|
||||
return item.(string)
|
||||
})
|
||||
|
||||
return form.NewForm(
|
||||
form.NewFieldText("Name").
|
||||
WithOptions(
|
||||
form.NewOption("label", "Your name"),
|
||||
form.NewOption("required", true),
|
||||
form.NewOption("row_attr", rowAttr),
|
||||
form.NewOption("label_attr", form.Attrs{"class": "required"}),
|
||||
).
|
||||
WithConstraints(validation.NewNotBlank()),
|
||||
form.NewFieldMail("Email").
|
||||
WithOptions(
|
||||
form.NewOption("label", "Your e-mail"),
|
||||
form.NewOption("required", true),
|
||||
form.NewOption("row_attr", rowAttr),
|
||||
form.NewOption("label_attr", form.Attrs{"class": "required"}),
|
||||
).
|
||||
WithConstraints(
|
||||
validation.NewNotBlank(),
|
||||
validation.NewMail(),
|
||||
),
|
||||
form.NewFieldChoice("Topic").
|
||||
WithOptions(
|
||||
form.NewOption("label", "Topic"),
|
||||
form.NewOption("empty_choice_label", ""),
|
||||
form.NewOption("choices", topics),
|
||||
),
|
||||
form.NewFieldTextarea("Message").
|
||||
WithOptions(
|
||||
form.NewOption("label", "Message"),
|
||||
form.NewOption("required", true),
|
||||
form.NewOption("row_attr", rowAttr),
|
||||
form.NewOption("label_attr", form.Attrs{"class": "required"}),
|
||||
form.NewOption("attr", form.Attrs{"rows": "10"}),
|
||||
form.NewOption("help_attr", form.Attrs{"class": "help"}),
|
||||
form.NewOption("help", "Minimum 10 chars"),
|
||||
).
|
||||
WithConstraints(
|
||||
validation.NewNotBlank(),
|
||||
validation.NewLength().WithMin(10),
|
||||
),
|
||||
form.NewSubmit("").
|
||||
WithData("Send").
|
||||
WithOptions(form.NewOption("attr", btnAttr)),
|
||||
).
|
||||
End().
|
||||
WithOptions(form.NewOption("attr", formAttr)).
|
||||
WithName("contact").
|
||||
WithMethod(http.MethodPost)
|
||||
}
|
||||
39
form/theme.go
Normal file
39
form/theme.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gitnet.fr/deblan/go-form/form"
|
||||
"gitnet.fr/deblan/go-form/validation"
|
||||
)
|
||||
|
||||
type Theme struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewTheme() *Theme {
|
||||
return &Theme{Value: "Html5"}
|
||||
}
|
||||
|
||||
func CreateThemeSelectorForm() *form.Form {
|
||||
choices := form.NewChoices([]string{"Html5", "Bootstrap5"})
|
||||
choices.LabelBuilder = func(key int, item any) string { return item.(string) }
|
||||
choices.ValueBuilder = func(key int, item any) string { return item.(string) }
|
||||
|
||||
return form.NewForm(
|
||||
form.NewFieldChoice("Value").
|
||||
WithOptions(
|
||||
form.NewOption("choices", choices),
|
||||
form.NewOption("label", "Select a theme"),
|
||||
form.NewOption("required", true),
|
||||
).
|
||||
WithConstraints(validation.NewNotBlank()),
|
||||
form.NewSubmit("").
|
||||
WithData("Change").
|
||||
WithOptions(form.NewOption("attr", form.Attrs{"class": "btn-primary"})),
|
||||
).
|
||||
End().
|
||||
WithName("").
|
||||
WithOptions(form.NewOption("attr", form.Attrs{"id": "theme-form"})).
|
||||
WithMethod(http.MethodGet)
|
||||
}
|
||||
11
go.mod
Normal file
11
go.mod
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module gitnet.fr/deblan/go-form-demo
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/spf13/cast v1.9.2 // indirect
|
||||
github.com/yassinebenaid/godump v0.11.1 // indirect
|
||||
gitnet.fr/deblan/go-form v1.1.6 // indirect
|
||||
maragu.dev/gomponents v1.1.0 // indirect
|
||||
)
|
||||
18
go.sum
Normal file
18
go.sum
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
|
||||
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/yassinebenaid/godump v0.11.1 h1:SPujx/XaYqGDfmNh7JI3dOyCUVrG0bG2duhO3Eh2EhI=
|
||||
github.com/yassinebenaid/godump v0.11.1/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44=
|
||||
gitnet.fr/deblan/go-form v1.1.2 h1:3jXPgJvgFGFfD3KFrp/l49luDTp6SNObdadVDxgsxvQ=
|
||||
gitnet.fr/deblan/go-form v1.1.2/go.mod h1:b+mDJlGUnFKnym06Mys5uHUskgo6uSlr1NMaQPwmJKY=
|
||||
gitnet.fr/deblan/go-form v1.1.3 h1:BvwYv1vb2wpvz2frUe+V6b4o2PXtzfdhYnV7+dRTtAM=
|
||||
gitnet.fr/deblan/go-form v1.1.3/go.mod h1:b+mDJlGUnFKnym06Mys5uHUskgo6uSlr1NMaQPwmJKY=
|
||||
gitnet.fr/deblan/go-form v1.1.4 h1:ULwjrmGPmvZNc/YwmNRLNKjweNGjozu2A5Z0D8Ol2XQ=
|
||||
gitnet.fr/deblan/go-form v1.1.4/go.mod h1:b+mDJlGUnFKnym06Mys5uHUskgo6uSlr1NMaQPwmJKY=
|
||||
gitnet.fr/deblan/go-form v1.1.5 h1:ad4StmMErs5Qrt8pzHOCLcaZyXjTupQtjZ9zsNH/qLQ=
|
||||
gitnet.fr/deblan/go-form v1.1.5/go.mod h1:b+mDJlGUnFKnym06Mys5uHUskgo6uSlr1NMaQPwmJKY=
|
||||
gitnet.fr/deblan/go-form v1.1.6 h1:ckT3jktWLnododHhiq0/rRLQQ66ccH+vofGfiCVslW8=
|
||||
gitnet.fr/deblan/go-form v1.1.6/go.mod h1:b+mDJlGUnFKnym06Mys5uHUskgo6uSlr1NMaQPwmJKY=
|
||||
maragu.dev/gomponents v1.1.0 h1:iCybZZChHr1eSlvkWp/JP3CrZGzctLudQ/JI3sBcO4U=
|
||||
maragu.dev/gomponents v1.1.0/go.mod h1:oEDahza2gZoXDoDHhw8jBNgH+3UR5ni7Ur648HORydM=
|
||||
14
main.go
Normal file
14
main.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"gitnet.fr/deblan/go-form-demo/controller"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", controller.UsingTemplate)
|
||||
|
||||
log.Fatal(http.ListenAndServe(":1324", nil))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue