diff --git a/README.md b/README.md index 0dfdfd5..eaccd3a 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ var inputTpl = ` - + {{with .Footer}}
{{.}}
{{end}} @@ -149,7 +149,7 @@ type Address struct { City string State string `form:"footer=Or your Province"` Zip string `form:"label=Postal Code"` - Country string + Country string `form:"class=specific-country-css-class"` } func main() { @@ -211,7 +211,7 @@ func main() { - + ``` diff --git a/builder_test.go b/builder_test.go index d360a1d..aa34015 100644 --- a/builder_test.go +++ b/builder_test.go @@ -11,7 +11,7 @@ import ( func TestBuilder_Inputs(t *testing.T) { tpl := template.Must(template.New("").Parse(strings.TrimSpace(` - + `))) tests := []struct { name string @@ -25,14 +25,31 @@ func TestBuilder_Inputs(t *testing.T) { arg: struct { Name string Email string `form:"type=email;placeholder=bob@example.com"` + City string `form:"class=custom-city-class"` }{ Name: "Michael Scott", + City: "New York", }, want: template.HTML(strings.Join([]string{ strings.TrimSpace(` `), strings.TrimSpace(` `), + strings.TrimSpace(` + `), + }, "")), + }, + { + name: "Custom attrs", + tpl: tpl, + arg: struct { + Street string `form:"attrs={\"ts-action\":\"class+ loading, 'parent .parent'\", \"ts-trigger\": \"click\"}"` + }{ + Street: "Mckinsey str.", + }, + want: template.HTML(strings.Join([]string{ + strings.TrimSpace(` + `), }, "")), }, } diff --git a/reflect.go b/reflect.go index d06a337..0e27598 100644 --- a/reflect.go +++ b/reflect.go @@ -1,6 +1,8 @@ package form import ( + "encoding/json" + "fmt" "html/template" "reflect" "strings" @@ -101,6 +103,12 @@ func applyTags(f *field, tags map[string]string) { // Probably shouldn't be HTML but whatever. f.Footer = template.HTML(v) } + if v, ok := tags["class"]; ok { + f.Class = template.HTMLEscapeString(v) + } + if v, ok := tags["attrs"]; ok { + f.Attrs = parseAttrs(v) + } } func parseTags(tags string) map[string]string { @@ -126,6 +134,26 @@ func parseTags(tags string) map[string]string { return ret } +func parseAttrs(attrs string) []attr { + attrs = strings.TrimSpace(attrs) + var ret = []attr{} + + if len(attrs) == 0 { + return ret + } + var attrVects map[string]string + + if err := json.Unmarshal([]byte(attrs), &attrVects); err != nil { + fmt.Errorf("Error unmarshaller json attributes: %v", err) + } + + for a, v := range attrVects { + ret = append(ret, attr{Attr: a, Value: v}) + } + + return ret +} + type field struct { Name string Label string @@ -134,4 +162,15 @@ type field struct { ID string Value interface{} Footer template.HTML + Class string + Attrs []attr +} + +type attr struct { + Attr string + Value string +} + +func (a *attr) Render() template.HTMLAttr { + return template.HTMLAttr(fmt.Sprintf(`%s="%s"`, a.Attr, a.Value)) } diff --git a/reflect_test.go b/reflect_test.go index 1e8c65a..d92cefb 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -196,6 +196,45 @@ func Test_fields(t *testing.T) { }, }, }, + { + name: "custom css class", + arg: struct { + Name string `form:"class=custom-css-class"` + }{ + Name: "Michael Scott", + }, + want: []field{ + { + Name: "Name", + Label: "Name", + Placeholder: "Name", + Type: "text", + Value: "Michael Scott", + Class: "custom-css-class", + }, + }, + }, + { + name: "custom attr", + arg: struct { + Name string `form:"attrs={\"ts-action\":\"class+ loading, 'parent .parent'\", \"ts-trigger\": \"click\"}"` + }{ + Name: "Michael Scott", + }, + want: []field{ + { + Name: "Name", + Label: "Name", + Placeholder: "Name", + Type: "text", + Value: "Michael Scott", + Attrs: []attr{ + attr{Attr: "ts-action", Value: "class+ loading, 'parent .parent'"}, + attr{Attr: "ts-trigger", Value: "click"}, + }, + }, + }, + }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) {