Merge pull request #664 from kioopi/renderform-textarea
Makes RenderForm use textarea-element when form type is `textarea`
Showing
2 changed files
with
94 additions
and
19 deletions
| ... | @@ -327,14 +327,6 @@ func ParseForm(form url.Values, obj interface{}) error { | ... | @@ -327,14 +327,6 @@ func ParseForm(form url.Values, obj interface{}) error { |
| 327 | return nil | 327 | return nil |
| 328 | } | 328 | } |
| 329 | 329 | ||
| 330 | // form types for RenderForm function | ||
| 331 | var FormType = map[string]bool{ | ||
| 332 | "text": true, | ||
| 333 | "textarea": true, | ||
| 334 | "hidden": true, | ||
| 335 | "password": true, | ||
| 336 | } | ||
| 337 | |||
| 338 | var unKind = map[reflect.Kind]bool{ | 330 | var unKind = map[reflect.Kind]bool{ |
| 339 | reflect.Uintptr: true, | 331 | reflect.Uintptr: true, |
| 340 | reflect.Complex64: true, | 332 | reflect.Complex64: true, |
| ... | @@ -368,15 +360,50 @@ func RenderForm(obj interface{}) template.HTML { | ... | @@ -368,15 +360,50 @@ func RenderForm(obj interface{}) template.HTML { |
| 368 | } | 360 | } |
| 369 | 361 | ||
| 370 | fieldT := objT.Field(i) | 362 | fieldT := objT.Field(i) |
| 363 | |||
| 364 | label, name, fType, ignored := parseFormTag(fieldT) | ||
| 365 | if ignored { | ||
| 366 | continue | ||
| 367 | } | ||
| 368 | |||
| 369 | raw = append(raw, renderFormField(label, name, fType, fieldV.Interface())) | ||
| 370 | } | ||
| 371 | return template.HTML(strings.Join(raw, "</br>")) | ||
| 372 | } | ||
| 373 | |||
| 374 | // renderFormField returns a string containing HTML of a single form field. | ||
| 375 | func renderFormField(label, name, fType string, value interface{}) string { | ||
| 376 | if isValidForInput(fType) { | ||
| 377 | return fmt.Sprintf(`%v<input name="%v" type="%v" value="%v">`, label, name, fType, value) | ||
| 378 | } | ||
| 379 | |||
| 380 | return fmt.Sprintf(`%v<%v name="%v">%v</%v>`, label, fType, name, value, fType) | ||
| 381 | } | ||
| 382 | |||
| 383 | // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. | ||
| 384 | func isValidForInput(fType string) bool { | ||
| 385 | 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") | ||
| 386 | for _, validType := range validInputTypes { | ||
| 387 | if fType == validType { | ||
| 388 | return true | ||
| 389 | } | ||
| 390 | } | ||
| 391 | return false | ||
| 392 | } | ||
| 393 | |||
| 394 | // parseFormTag takes the stuct-tag of a StructField and parses the `form` value. | ||
| 395 | // returned are the form label, name-property, type and wether the field should be ignored. | ||
| 396 | func parseFormTag(fieldT reflect.StructField) (label, name, fType string, ignored bool) { | ||
| 371 | tags := strings.Split(fieldT.Tag.Get("form"), ",") | 397 | tags := strings.Split(fieldT.Tag.Get("form"), ",") |
| 372 | label := fieldT.Name + ": " | 398 | label = fieldT.Name + ": " |
| 373 | name := fieldT.Name | 399 | name = fieldT.Name |
| 374 | fType := "text" | 400 | fType = "text" |
| 401 | ignored = false; | ||
| 375 | 402 | ||
| 376 | switch len(tags) { | 403 | switch len(tags) { |
| 377 | case 1: | 404 | case 1: |
| 378 | if tags[0] == "-" { | 405 | if tags[0] == "-" { |
| 379 | continue | 406 | ignored = true |
| 380 | } | 407 | } |
| 381 | if len(tags[0]) > 0 { | 408 | if len(tags[0]) > 0 { |
| 382 | name = tags[0] | 409 | name = tags[0] |
| ... | @@ -399,11 +426,7 @@ func RenderForm(obj interface{}) template.HTML { | ... | @@ -399,11 +426,7 @@ func RenderForm(obj interface{}) template.HTML { |
| 399 | label = tags[2] | 426 | label = tags[2] |
| 400 | } | 427 | } |
| 401 | } | 428 | } |
| 402 | 429 | return | |
| 403 | raw = append(raw, fmt.Sprintf(`%v<input name="%v" type="%v" value="%v">`, | ||
| 404 | label, name, fType, fieldV.Interface())) | ||
| 405 | } | ||
| 406 | return template.HTML(strings.Join(raw, "</br>")) | ||
| 407 | } | 430 | } |
| 408 | 431 | ||
| 409 | func isStructPtr(t reflect.Type) bool { | 432 | func isStructPtr(t reflect.Type) bool { | ... | ... |
| ... | @@ -15,6 +15,7 @@ import ( | ... | @@ -15,6 +15,7 @@ import ( |
| 15 | "net/url" | 15 | "net/url" |
| 16 | "testing" | 16 | "testing" |
| 17 | "time" | 17 | "time" |
| 18 | "reflect" | ||
| 18 | ) | 19 | ) |
| 19 | 20 | ||
| 20 | func TestSubstr(t *testing.T) { | 21 | func TestSubstr(t *testing.T) { |
| ... | @@ -147,9 +148,10 @@ func TestRenderForm(t *testing.T) { | ... | @@ -147,9 +148,10 @@ func TestRenderForm(t *testing.T) { |
| 147 | Sex string | 148 | Sex string |
| 148 | Email []string | 149 | Email []string |
| 149 | Intro string `form:",textarea"` | 150 | Intro string `form:",textarea"` |
| 151 | Ignored string `form:"-"` | ||
| 150 | } | 152 | } |
| 151 | 153 | ||
| 152 | u := user{Name: "test"} | 154 | u := user{Name: "test", Intro: "Some Text"} |
| 153 | output := RenderForm(u) | 155 | output := RenderForm(u) |
| 154 | if output != template.HTML("") { | 156 | if output != template.HTML("") { |
| 155 | t.Errorf("output should be empty but got %v", output) | 157 | t.Errorf("output should be empty but got %v", output) |
| ... | @@ -159,8 +161,58 @@ func TestRenderForm(t *testing.T) { | ... | @@ -159,8 +161,58 @@ func TestRenderForm(t *testing.T) { |
| 159 | `Name: <input name="username" type="text" value="test"></br>` + | 161 | `Name: <input name="username" type="text" value="test"></br>` + |
| 160 | `年龄:<input name="age" type="text" value="0"></br>` + | 162 | `年龄:<input name="age" type="text" value="0"></br>` + |
| 161 | `Sex: <input name="Sex" type="text" value=""></br>` + | 163 | `Sex: <input name="Sex" type="text" value=""></br>` + |
| 162 | `Intro: <input name="Intro" type="textarea" value="">`) | 164 | `Intro: <textarea name="Intro">Some Text</textarea>`) |
| 163 | if output != result { | 165 | if output != result { |
| 164 | t.Errorf("output should equal `%v` but got `%v`", result, output) | 166 | t.Errorf("output should equal `%v` but got `%v`", result, output) |
| 165 | } | 167 | } |
| 166 | } | 168 | } |
| 169 | |||
| 170 | func TestRenderFormField(t *testing.T) { | ||
| 171 | html := renderFormField("Label: ", "Name", "text", "Value") | ||
| 172 | if html != `Label: <input name="Name" type="text" value="Value">` { | ||
| 173 | t.Errorf("Wrong html output for input[type=text]: %v ", html) | ||
| 174 | } | ||
| 175 | |||
| 176 | html = renderFormField("Label: ", "Name", "textarea", "Value") | ||
| 177 | if html != `Label: <textarea name="Name">Value</textarea>` { | ||
| 178 | t.Errorf("Wrong html output for textarea: %v ", html) | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | func TestParseFormTag(t *testing.T) { | ||
| 183 | // create struct to contain field with different types of struct-tag `form` | ||
| 184 | type user struct { | ||
| 185 | All int `form:"name,text,年龄:"` | ||
| 186 | NoName int `form:",hidden,年龄:"` | ||
| 187 | OnlyLabel int `form:",,年龄:"` | ||
| 188 | OnlyName int `form:"name"` | ||
| 189 | Ignored int `form:"-"` | ||
| 190 | } | ||
| 191 | |||
| 192 | objT := reflect.TypeOf(&user{}).Elem() | ||
| 193 | |||
| 194 | label, name, fType, ignored := parseFormTag(objT.Field(0)) | ||
| 195 | if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) { | ||
| 196 | t.Errorf("Form Tag with name, label and type was not correctly parsed.") | ||
| 197 | } | ||
| 198 | |||
| 199 | label, name, fType, ignored = parseFormTag(objT.Field(1)) | ||
| 200 | if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) { | ||
| 201 | t.Errorf("Form Tag with label and type but without name was not correctly parsed.") | ||
| 202 | } | ||
| 203 | |||
| 204 | label, name, fType, ignored = parseFormTag(objT.Field(2)) | ||
| 205 | if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) { | ||
| 206 | t.Errorf("Form Tag containing only label was not correctly parsed.") | ||
| 207 | } | ||
| 208 | |||
| 209 | label, name, fType, ignored = parseFormTag(objT.Field(3)) | ||
| 210 | if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false) { | ||
| 211 | t.Errorf("Form Tag containing only name was not correctly parsed.") | ||
| 212 | } | ||
| 213 | |||
| 214 | label, name, fType, ignored = parseFormTag(objT.Field(4)) | ||
| 215 | if (ignored == false) { | ||
| 216 | t.Errorf("Form Tag that should be ignored was not correctly parsed.") | ||
| 217 | } | ||
| 218 | } | ... | ... |
-
Please register or sign in to post a comment