diff --git a/templatefunc.go b/templatefunc.go index 84624d2e..274e3af9 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -327,14 +327,6 @@ func ParseForm(form url.Values, obj interface{}) error { return nil } -// form types for RenderForm function -var FormType = map[string]bool{ - "text": true, - "textarea": true, - "hidden": true, - "password": true, -} - var unKind = map[reflect.Kind]bool{ reflect.Uintptr: true, reflect.Complex64: true, @@ -368,44 +360,75 @@ func RenderForm(obj interface{}) template.HTML { } fieldT := objT.Field(i) - tags := strings.Split(fieldT.Tag.Get("form"), ",") - label := fieldT.Name + ": " - name := fieldT.Name - fType := "text" - switch len(tags) { - case 1: - if tags[0] == "-" { - continue - } - if len(tags[0]) > 0 { - name = tags[0] - } - case 2: - if len(tags[0]) > 0 { - name = tags[0] - } - if len(tags[1]) > 0 { - fType = tags[1] - } - case 3: - if len(tags[0]) > 0 { - name = tags[0] - } - if len(tags[1]) > 0 { - fType = tags[1] - } - if len(tags[2]) > 0 { - label = tags[2] - } - } + label, name, fType, ignored := parseFormTag(fieldT) + if ignored { + continue + } - raw = append(raw, fmt.Sprintf(`%v`, - label, name, fType, fieldV.Interface())) + raw = append(raw, renderFormField(label, name, fType, fieldV.Interface())) } return template.HTML(strings.Join(raw, "
")) } +// renderFormField returns a string containing HTML of a single form field. +func renderFormField(label, name, fType string, value interface{}) string { + if isValidForInput(fType) { + return fmt.Sprintf(`%v`, label, name, fType, value) + } + + return fmt.Sprintf(`%v<%v name="%v">%v`, label, fType, name, value, fType) +} + +// isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. +func isValidForInput(fType string) bool { + validInputTypes := strings.Fields("text password checkbox radio submit reset hidden image file button search email url tel number range date month week time datetime datetime-local color") + for _, validType := range validInputTypes { + if fType == validType { + return true + } + } + return false +} + +// parseFormTag takes the stuct-tag of a StructField and parses the `form` value. +// returned are the form label, name-property, type and wether the field should be ignored. +func parseFormTag(fieldT reflect.StructField) (label, name, fType string, ignored bool) { + tags := strings.Split(fieldT.Tag.Get("form"), ",") + label = fieldT.Name + ": " + name = fieldT.Name + fType = "text" + ignored = false; + + switch len(tags) { + case 1: + if tags[0] == "-" { + ignored = true + } + if len(tags[0]) > 0 { + name = tags[0] + } + case 2: + if len(tags[0]) > 0 { + name = tags[0] + } + if len(tags[1]) > 0 { + fType = tags[1] + } + case 3: + if len(tags[0]) > 0 { + name = tags[0] + } + if len(tags[1]) > 0 { + fType = tags[1] + } + if len(tags[2]) > 0 { + label = tags[2] + } + } + return +} + func isStructPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct } diff --git a/templatefunc_test.go b/templatefunc_test.go index c3879fbb..373b11fa 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -15,6 +15,7 @@ import ( "net/url" "testing" "time" + "reflect" ) func TestSubstr(t *testing.T) { @@ -147,9 +148,10 @@ func TestRenderForm(t *testing.T) { Sex string Email []string Intro string `form:",textarea"` + Ignored string `form:"-"` } - u := user{Name: "test"} + u := user{Name: "test", Intro: "Some Text"} output := RenderForm(u) if output != template.HTML("") { t.Errorf("output should be empty but got %v", output) @@ -159,8 +161,58 @@ func TestRenderForm(t *testing.T) { `Name:
` + `年龄:
` + `Sex:
` + - `Intro: `) + `Intro: `) if output != result { t.Errorf("output should equal `%v` but got `%v`", result, output) } } + +func TestRenderFormField(t *testing.T) { + html := renderFormField("Label: ", "Name", "text", "Value") + if html != `Label: ` { + t.Errorf("Wrong html output for input[type=text]: %v ", html) + } + + html = renderFormField("Label: ", "Name", "textarea", "Value") + if html != `Label: ` { + t.Errorf("Wrong html output for textarea: %v ", html) + } +} + +func TestParseFormTag(t *testing.T) { + // create struct to contain field with different types of struct-tag `form` + type user struct { + All int `form:"name,text,年龄:"` + NoName int `form:",hidden,年龄:"` + OnlyLabel int `form:",,年龄:"` + OnlyName int `form:"name"` + Ignored int `form:"-"` + } + + objT := reflect.TypeOf(&user{}).Elem() + + label, name, fType, ignored := parseFormTag(objT.Field(0)) + if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) { + t.Errorf("Form Tag with name, label and type was not correctly parsed.") + } + + label, name, fType, ignored = parseFormTag(objT.Field(1)) + if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) { + t.Errorf("Form Tag with label and type but without name was not correctly parsed.") + } + + label, name, fType, ignored = parseFormTag(objT.Field(2)) + if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) { + t.Errorf("Form Tag containing only label was not correctly parsed.") + } + + label, name, fType, ignored = parseFormTag(objT.Field(3)) + if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false) { + t.Errorf("Form Tag containing only name was not correctly parsed.") + } + + label, name, fType, ignored = parseFormTag(objT.Field(4)) + if (ignored == false) { + t.Errorf("Form Tag that should be ignored was not correctly parsed.") + } +}