From ce0750cfda5e207a03dacd7bf115cfc6e2ce367f Mon Sep 17 00:00:00 2001 From: Steve Manuel Date: Thu, 29 Sep 2016 19:08:58 -0400 Subject: adding Select and Checkbox elements as well as necessary formatting functions. Needed to modify the valueFromStructField to return the more generic reflect.Value to then be cast to a type as necessary. Additionally, added a modified version of the tagNameFromStruct method for multi-value form inputs like checkbox and the way gorilla/schema package handles them --- management/editor/elements.go | 238 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 226 insertions(+), 12 deletions(-) diff --git a/management/editor/elements.go b/management/editor/elements.go index 7c1c429..c1c418b 100644 --- a/management/editor/elements.go +++ b/management/editor/elements.go @@ -2,9 +2,19 @@ package editor import ( "bytes" + "fmt" "reflect" ) +type element struct { + TagName string + Attrs map[string]string + Name string + label string + data string + viewBuf *bytes.Buffer +} + // Input returns the []byte of an HTML element with a label. // IMPORTANT: // The `fieldName` argument will cause a panic if it is not exactly the string @@ -25,20 +35,128 @@ func Textarea(fieldName string, p interface{}, attrs map[string]string) []byte { return domElement(e) } -type element struct { - TagName string - Attrs map[string]string - Name string - label string - data string - viewBuf *bytes.Buffer +// Select returns the []byte of a HTML elements +// wrapped in a
with a label. +// IMPORTANT: +// The `fieldName` argument will cause a panic if it is not exactly the string +// form of the struct field that this editor input is representing +func Checkbox(fieldName string, p interface{}, attrs, options map[string]string) []byte { + // options are the value attr and the display value, i.e. + /* + + */ + + div := newElement("div", attrs["label"], "", p, attrs) + var opts []*element + + // get the pre-checked options if this is already an existing post + checkedVals := valueFromStructField(fieldName, p) // returns refelct.Value + checked := checkedVals.Slice(0, checkedVals.Len()).Interface().([]string) // casts reflect.Value to []string + + i := 0 + for k, v := range options { + // check if k is in the pre-checked values and set to checked + var val string + for _, x := range checked { + if k == x { + val = "true" + } + } + + // create a *element manually using the maodified tagNameFromStructFieldMulti + // func since this is for a multi-value name + input := &element{ + TagName: "input", + Attrs: map[string]string{ + "type": "checkbox", + "checked": val, + "value": k, + }, + Name: tagNameFromStructFieldMulti(fieldName, i, p), + label: v, + data: "", + viewBuf: &bytes.Buffer{}, + } + + // if checked == false, delete it from input.Attrs for clarity + if input.Attrs["checked"] == "" { + delete(input.Attrs, "checked") + } + + opts = append(opts, input) + i++ + } + + return domElementWithChildrenCheckbox(div, opts) } // domElementSelfClose is a special DOM element which is parsed as a // self-closing tag and thus needs to be created differently func domElementSelfClose(e *element) []byte { if e.label != "" { - e.viewBuf.Write([]byte(``)) + e.viewBuf.Write([]byte(``)) + } + + return e.viewBuf.Bytes() +} + +// domElementCheckbox is a special DOM element which is parsed as a +// checkbox input tag and thus needs to be created differently +func domElementCheckbox(e *element) []byte { + if e.label != "" { + e.viewBuf.Write([]byte(``)) + } + return e.viewBuf.Bytes() } // domElement creates a DOM element func domElement(e *element) []byte { if e.label != "" { - e.viewBuf.Write([]byte(``)) + e.viewBuf.Write([]byte(``)) + } + + return e.viewBuf.Bytes() +} + +func domElementWithChildren(e *element, children []*element) []byte { + if e.label != "" { + e.viewBuf.Write([]byte(``)) + } + + return e.viewBuf.Bytes() +} + +func domElementWithChildrenCheckbox(e *element, children []*element) []byte { + if e.label != "" { + e.viewBuf.Write([]byte(``)) + } + return e.viewBuf.Bytes() } func tagNameFromStructField(name string, post interface{}) string { + // sometimes elements in these environments will not have a name, + // and thus no tag name in the struct which correlates to it. + if name == "" { + return name + } + field, ok := reflect.TypeOf(post).Elem().FieldByName(name) if !ok { panic("Couldn't get struct field for: " + name + ". Make sure you pass the right field name to editor field elements.") @@ -85,10 +290,19 @@ func tagNameFromStructField(name string, post interface{}) string { return tag } -func valueFromStructField(name string, post interface{}) string { +// due to the format in which gorilla/schema expects form names to be when +// one is associated with multiple values, we need to output the name as such. +// Ex. 'category.0', 'category.1', 'category.2' and so on. +func tagNameFromStructFieldMulti(name string, i int, post interface{}) string { + tag := tagNameFromStructField(name, post) + + return fmt.Sprintf("%s.%d", tag, i) +} + +func valueFromStructField(name string, post interface{}) reflect.Value { field := reflect.Indirect(reflect.ValueOf(post)).FieldByName(name) - return field.String() + return field } func newElement(tagName, label, fieldName string, p interface{}, attrs map[string]string) *element { @@ -97,7 +311,7 @@ func newElement(tagName, label, fieldName string, p interface{}, attrs map[strin Attrs: attrs, Name: tagNameFromStructField(fieldName, p), label: label, - data: valueFromStructField(fieldName, p), + data: valueFromStructField(fieldName, p).String(), viewBuf: &bytes.Buffer{}, } } -- cgit v1.2.3