Skip to content

Commit

Permalink
Merge pull request #106 from larrymjordan/sets_array_to_form_value
Browse files Browse the repository at this point in the history
Support for nested slices and arrays
  • Loading branch information
paganotoni authored Aug 23, 2018
2 parents 0e9ca01 + 9702ef3 commit f854d1c
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 5 deletions.
27 changes: 24 additions & 3 deletions form/form_for.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"database/sql/driver"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
"sync"

Expand All @@ -13,6 +15,8 @@ import (
"github.com/markbates/inflect"
)

var arrayFieldRegExp = regexp.MustCompile("^([A-Za-z0-9]+)\\[(\\d+)\\]$")

//FormFor is a form made for a struct
type FormFor struct {
*Form
Expand Down Expand Up @@ -186,16 +190,33 @@ func (f FormFor) value(field string) interface{} {

if fn.IsValid() == false {
dots := strings.Split(field, ".")
if len(dots) == 1 {

if len(dots) == 1 && !arrayFieldRegExp.Match([]byte(dots[0])) {
if !strings.HasSuffix(field, "ID") {
return f.value(field + "ID")
}
return ""
}

matches := arrayFieldRegExp.FindStringSubmatch(dots[0])
if len(matches) != 0 {
dots[0] = matches[1]
}

fn = f.reflection.FieldByName(dots[0])

if fn.IsValid() {
ff := NewFormFor(fn.Interface(), f.Options)
return ff.value(strings.Join(dots[1:], "."))
fn = reflect.Indirect(fn)

if fn.Kind() == reflect.Slice || fn.Kind() == reflect.Array {
index, _ := strconv.Atoi(matches[2])
fn = reflect.Indirect(fn.Index(index))
}

if fn.Kind() == reflect.Struct {
ff := NewFormFor(fn.Interface(), f.Options)
return ff.value(strings.Join(dots[1:], "."))
}
}
}

Expand Down
119 changes: 117 additions & 2 deletions form/form_for_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package form_test

import (
"database/sql"
"strconv"
"testing"
"time"

Expand Down Expand Up @@ -159,8 +160,10 @@ func Test_FormFor_NullableField(t *testing.T) {
}

type Person struct {
Name string
Address Address
Name string
Address Address
References []Address
Contacts []string
}

type Address struct {
Expand All @@ -185,6 +188,118 @@ func Test_FormFor_Nested_Struct(t *testing.T) {
r.Equal(exp, tag.String())
}

func Test_FormFor_Nested_Slice_Struct(t *testing.T) {
r := require.New(t)
p := Person{
Name: "Mark",
Address: Address{
City: "Boston",
State: "MA",
},
}
p.References = []Address{p.Address}

f := form.NewFormFor(p, tags.Options{})
tag := f.InputTag("References[0].City", tags.Options{})

exp := `<input id="person-References[0].City" name="References[0].City" type="text" value="Boston" />`
r.Equal(exp, tag.String())
}

func Test_FormFor_Nested_Slice_String(t *testing.T) {
r := require.New(t)
p := Person{
Contacts: []string{
"Contact 1",
"Contact 2",
"Contact 3",
},
}

f := form.NewFormFor(p, tags.Options{})

for i := 0; i < len(p.Contacts); i++ {
expectedValue := p.Contacts[i]
index := strconv.Itoa(i)
tag := f.InputTag("Contacts["+index+"]", tags.Options{})
exp := `<input id="person-Contacts[` + index + `]" name="Contacts[` + index + `]" type="text" value="` + expectedValue + `" />`
r.Equal(exp, tag.String())
}
}

func Test_FormFor_Nested_Slice_String_Pointer(t *testing.T) {
r := require.New(t)
p := struct {
Contacts *[]string
}{
&[]string{"Contact 1", "Contact 2"},
}

f := form.NewFormFor(p, tags.Options{})
tag := f.InputTag("Contacts[0]", tags.Options{})

exp := `<input id="-Contacts[0]" name="Contacts[0]" type="text" value="Contact 1" />`
r.Equal(exp, tag.String())
}

func Test_FormFor_Nested_Slice_Pointer(t *testing.T) {
r := require.New(t)
p := struct {
Persons *[]Person
}{
&[]Person{{Name: "Mark"}, {Name: "Clayton"}, {Name: "Iain"}},
}

f := form.NewFormFor(p, tags.Options{})

for i := 0; i < len(*p.Persons); i++ {
expectedValue := (*p.Persons)[i].Name
index := strconv.Itoa(i)
tag := f.InputTag("Persons["+index+"].Name", tags.Options{})
exp := `<input id="-Persons[` + index + `].Name" name="Persons[` + index + `].Name" type="text" value="` + expectedValue + `" />`
r.Equal(exp, tag.String())
}
}

func Test_FormFor_Nested_Slice_Pointer_Elements(t *testing.T) {
r := require.New(t)
p := struct {
Persons []*Person
}{
[]*Person{
&Person{Name: "Mark"},
},
}

f := form.NewFormFor(p, tags.Options{})
tag := f.InputTag("Persons[0].Name", tags.Options{})

exp := `<input id="-Persons[0].Name" name="Persons[0].Name" type="text" value="Mark" />`
r.Equal(exp, tag.String())
}

func Test_FormFor_Nested_Slice_With_Sub_Slices(t *testing.T) {
r := require.New(t)
p := struct {
Persons *[]Person
}{
&[]Person{
{
Name: "Mark",
References: []Address{
{City: "Boston"},
},
},
},
}

f := form.NewFormFor(p, tags.Options{})
tag := f.InputTag("Persons[0].References[0].City", tags.Options{})

exp := `<input id="-Persons[0].References[0].City" name="Persons[0].References[0].City" type="text" value="Boston" />`
r.Equal(exp, tag.String())
}

func Test_FormFor_DateTimeTag(t *testing.T) {
r := require.New(t)

Expand Down

0 comments on commit f854d1c

Please sign in to comment.