Merge pull request #664 from kioopi/renderform-textarea
Makes RenderForm use textarea-element when form type is `textarea`
Showing
2 changed files
with
118 additions
and
43 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,44 +360,75 @@ func RenderForm(obj interface{}) template.HTML { | ... | @@ -368,44 +360,75 @@ func RenderForm(obj interface{}) template.HTML { |
| 368 | } | 360 | } |
| 369 | 361 | ||
| 370 | fieldT := objT.Field(i) | 362 | fieldT := objT.Field(i) |
| 371 | tags := strings.Split(fieldT.Tag.Get("form"), ",") | ||
| 372 | label := fieldT.Name + ": " | ||
| 373 | name := fieldT.Name | ||
| 374 | fType := "text" | ||
| 375 | |||
| 376 | switch len(tags) { | ||
| 377 | case 1: | ||
| 378 | if tags[0] == "-" { | ||
| 379 | continue | ||
| 380 | } | ||
| 381 | if len(tags[0]) > 0 { | ||
| 382 | name = tags[0] | ||
| 383 | } | ||
| 384 | case 2: | ||
| 385 | if len(tags[0]) > 0 { | ||
| 386 | name = tags[0] | ||
| 387 | } | ||
| 388 | if len(tags[1]) > 0 { | ||
| 389 | fType = tags[1] | ||
| 390 | } | ||
| 391 | case 3: | ||
| 392 | if len(tags[0]) > 0 { | ||
| 393 | name = tags[0] | ||
| 394 | } | ||
| 395 | if len(tags[1]) > 0 { | ||
| 396 | fType = tags[1] | ||
| 397 | } | ||
| 398 | if len(tags[2]) > 0 { | ||
| 399 | label = tags[2] | ||
| 400 | } | ||
| 401 | } | ||
| 402 | 363 | ||
| 403 | raw = append(raw, fmt.Sprintf(`%v<input name="%v" type="%v" value="%v">`, | 364 | label, name, fType, ignored := parseFormTag(fieldT) |
| 404 | label, name, fType, fieldV.Interface())) | 365 | if ignored { |
| 366 | continue | ||
| 367 | } | ||
| 368 | |||
| 369 | raw = append(raw, renderFormField(label, name, fType, fieldV.Interface())) | ||
| 405 | } | 370 | } |
| 406 | return template.HTML(strings.Join(raw, "</br>")) | 371 | return template.HTML(strings.Join(raw, "</br>")) |
| 407 | } | 372 | } |
| 408 | 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) { | ||
| 397 | tags := strings.Split(fieldT.Tag.Get("form"), ",") | ||
| 398 | label = fieldT.Name + ": " | ||
| 399 | name = fieldT.Name | ||
| 400 | fType = "text" | ||
| 401 | ignored = false; | ||
| 402 | |||
| 403 | switch len(tags) { | ||
| 404 | case 1: | ||
| 405 | if tags[0] == "-" { | ||
| 406 | ignored = true | ||
| 407 | } | ||
| 408 | if len(tags[0]) > 0 { | ||
| 409 | name = tags[0] | ||
| 410 | } | ||
| 411 | case 2: | ||
| 412 | if len(tags[0]) > 0 { | ||
| 413 | name = tags[0] | ||
| 414 | } | ||
| 415 | if len(tags[1]) > 0 { | ||
| 416 | fType = tags[1] | ||
| 417 | } | ||
| 418 | case 3: | ||
| 419 | if len(tags[0]) > 0 { | ||
| 420 | name = tags[0] | ||
| 421 | } | ||
| 422 | if len(tags[1]) > 0 { | ||
| 423 | fType = tags[1] | ||
| 424 | } | ||
| 425 | if len(tags[2]) > 0 { | ||
| 426 | label = tags[2] | ||
| 427 | } | ||
| 428 | } | ||
| 429 | return | ||
| 430 | } | ||
| 431 | |||
| 409 | func isStructPtr(t reflect.Type) bool { | 432 | func isStructPtr(t reflect.Type) bool { |
| 410 | return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct | 433 | return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct |
| 411 | } | 434 | } | ... | ... |
| ... | @@ -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