Skip to content

Instantly share code, notes, and snippets.

@kylelemons
Created February 16, 2013 17:20
Show Gist options
  • Save kylelemons/4967786 to your computer and use it in GitHub Desktop.
Save kylelemons/4967786 to your computer and use it in GitHub Desktop.
Quick and dirty example of HTML forms with validation dropping data into a struct. Note that the scanf should probably be replaced with a type switch, because it likes tokenized input.
package main
import (
"fmt"
"html/template"
"net/url"
"os"
"regexp"
)
type FormElement interface {
HTML() template.HTML
Validate(url.Values) error
}
type TextInput struct {
ID string
Output interface{}
Valid *regexp.Regexp
}
func (t *TextInput) HTML() template.HTML {
form := fmt.Sprintf(`<input type=%q id=%q name=%q />`, "text", t.ID, t.ID)
return template.HTML(form)
}
func (t *TextInput) Validate(form url.Values) error {
val := form.Get(t.ID)
if !t.Valid.MatchString(val) {
return fmt.Errorf("%q does not match /%s/", val, t.Valid)
}
if len(val) == 0 {
return nil
}
if _, err := fmt.Sscan(val, t.Output); err != nil {
return fmt.Errorf("%q cannot be stored in %T: %s", val, t.Output, err)
}
return nil
}
type Address struct {
Name string
Street [2]string
City, State string
Zip int
}
func (a *Address) Form() map[string]FormElement {
return map[string]FormElement{
"name": &TextInput{"nm", &a.Name, regexp.MustCompile(`\S+\s+\S+`)},
"street1": &TextInput{"sn", &a.Street[0], regexp.MustCompile(`\d+.*\S+`)},
"street2": &TextInput{"sx", &a.Street[1], regexp.MustCompile(`|\S+`)},
"city": &TextInput{"ct", &a.City, regexp.MustCompile(`\S+`)},
"state": &TextInput{"st", &a.State, regexp.MustCompile(`\S{2}`)},
"zip": &TextInput{"zp", &a.Zip, regexp.MustCompile(`\d{5}(-\d{4})?`)},
}
}
func main() {
var a Address
form := a.Form()
tpl := template.Must(template.New("").Parse("{{.name.HTML}}\n{{.street1.HTML}}\n...\n\n"))
tpl.Execute(os.Stdout, form)
reply := url.Values{
"nm": {"Zaphod Beeblebrox"},
"sn": {"42 Heart of Gold"},
"ct": {"Betelgeuse Central"},
"st": {"BG"},
"zp": {"42069"},
}
for id, f := range form {
if err := f.Validate(reply); err != nil {
fmt.Printf("%q - %s\n", id, err)
}
}
fmt.Printf("%#v", a)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment