Skip to content

Instantly share code, notes, and snippets.

Created May 18, 2011 11:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anonymous/978407 to your computer and use it in GitHub Desktop.
Save anonymous/978407 to your computer and use it in GitHub Desktop.
render form
package web
import (
"reflect"
"strconv"
"strings"
"util"
)
const (
Create = iota
Edit
Show
)
//Render type
const (
RT_select = iota
RT_input
RT_text
RT_textarea
RT_hidden
)
type View struct {
Name string
Msg string
Fields []Field
}
type Form struct {
Name string
Action string
Type int //Create(0), Edit(1) ...
Msg string
Fields []Field
}
//TODO: may merge to FieldAttr
type Option struct {
Value string
Selected bool
}
type FieldAttr struct {
BoolAttr map[string]bool
StrAttr map[string]string
}
type Field struct {
Name, Value, Type string
RenderType int
ErrorType int
ErrorMsg string
Options []Option
FieldAttr FieldAttr
}
func indirect(v reflect.Value) reflect.Value {
for {
pv := v
if pv.Kind() != reflect.Ptr {
break
}
if pv.IsNil() {
pv.Set(reflect.Zero(pv.Type().Elem()).Addr())
}
v = pv.Elem()
}
return v
}
func handleFieldValue(v reflect.Value) string {
var val string
switch av := v; av.Kind() {
case reflect.Bool:
val = strconv.Btoa(av.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val = strconv.Itoa64(av.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
val = strconv.Uitoa64(uint64(av.Uint()))
case reflect.Float32, reflect.Float64:
if av.Type().Size() == 4 {
val = strconv.Ftoa32(float32(av.Float()), 'f', 2)
} else {
val = strconv.Ftoa64(float64(av.Float()), 'f', 2)
}
case reflect.String:
val = av.String()
default:
println("default : unsupported type !!")
}
return val
}
func handleField(v reflect.Value, f reflect.StructField) Field {
var field Field
field.Name = f.Name
field.Type = f.Type.Name()
v = indirect(v)
switch av := v; av.Kind() {
case reflect.Bool:
field.Value = strconv.Btoa(av.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
field.Value = strconv.Itoa64(av.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
field.Value = strconv.Uitoa64(uint64(av.Uint()))
case reflect.Float32, reflect.Float64:
if av.Type().Size() == 4 {
field.Value = strconv.Ftoa32(float32(av.Float()), 'f', 2)
} else {
field.Value = strconv.Ftoa64(float64(av.Float()), 'f', 2)
}
case reflect.String:
field.Value = av.String()
case reflect.Map:
println("MapValue")
case reflect.Array, reflect.Slice:
println("ArrayOrSliceValue", av.Len())
for i := 0; i < av.Len(); i++ {
field.Options = append(field.Options, Option{handleFieldValue(av.Index(i)), true})
}
case reflect.Ptr:
println("PtrValue")
case reflect.Struct:
println("StructValue")
default:
println("default : unknown type for ", f.Name)
}
return field
}
func ShouldExclude(f string, e ...string) bool {
for _, val := range e {
if val == f {
return true
}
}
return false
}
func NewForm(i interface{}, exclude ...string) *Form {
var form Form
v := indirect(reflect.NewValue(i))
switch v.Kind() {
case reflect.Struct:
t := v.Type()
form.Name = t.Name()
form.Fields = []Field{}
for i := 0; i < v.NumField(); i++ {
f := t.Field(i)
if !ShouldExclude(f.Name, exclude...) {
fv := v.Field(i)
field := handleField(fv, f)
field.RenderType = RT_input //default
form.Fields = append(form.Fields, field)
}
}
default:
//util.Logger.Println("New: input needs to be struct type")
println("New: input needs to be struct type")
}
return &form
}
func (f *Field) renderSelect() string {
name := strings.ToLower(f.Name)
//text := "\t\t<span class=\"field\">\n"
text := "\t\t<label for=" + name + ">" + f.Name + ":</label>\n"
text += "\t\t<select name=\"" + name + "\">\n"
for _, o := range f.Options {
if o.Selected {
text += "\t\t<option selected=\"selected\">" + o.Value + "</option>\n"
} else {
text += "\t\t<option>" + o.Value + "</option>\n"
}
}
text += "\t\t</select>\n"
// text += "\t\t</span>\n\n"
return text
}
func (f *Field) renderInput() string {
//name := strings.ToLower(f.Name)
text := "\t\t<span class=\"field\">\n"
text += "\t\t<label for=" + f.Name + ">" + f.Name + ":</label>\n"
text += "\t\t<input type=text id=" + f.Name + " name=" + f.Name + " value=" + f.Value + ">\n"
if f.ErrorType != util.NOERROR {
text += "\t\t<span class=\"errormsg\">" + f.ErrorMsg + "</span>\n"
}
text += "\t\t</span>\n\n"
return text
}
func (f *Field) renderTextarea() string {
text := "\t\t<span class=\"field\">\n"
text += "\t\t<label for=" + f.Name + ">" + f.Name + ":</label>\n"
text += "\t\t<textarea id=" + f.Name + " name=" + f.Name + " value=" + f.Value + "></textarea>\n"
text += "\t\t</span>\n\n"
return text
}
func (f *Field) renderHidden() string {
return "\t\t<input type=hidden id=" + f.Name + " name=" + f.Name + " value=" + f.Value + ">\n"
}
//TODO: legend
func (f *Form) Render() string {
var form string
form += "<form id=ui.form." + f.Name + " method=\"post\" action=\"" + f.Action + "\">\n\t<fieldset>\n\t<legend>Enter details</legend>\n\n"
for _, v := range f.Fields {
form += v.render()
}
if f.Type == Create {
form += "\t</fieldset>\n\t<fieldset>\n\t\t<button type=submit>Create</button>\n\t</fieldset>\n"
} else {
form += "\t</fieldset>\n\t<fieldset>\n\t\t<button type=submit>Save</button>\n\t</fieldset>\n"
}
form += "\t</form>"
return form
}
//TODO: not sure about setting selection for multiple select
func (f *Form) SetOptionField(fn string, o []Option, preserve bool) {
for i := 0; i < len(f.Fields); i++ {
v := &f.Fields[i]
if v.Name == fn {
v.RenderType = RT_select
if preserve {
for i := range o {
c := &o[i]
if v.Value == c.Value {
c.Selected = true
}
}
}
v.Options = o
}
}
}
func (f *Form) MapValidation(val util.Reply) {
for _, vf := range val.GetFields() {
//for _, ff := range f.Fields { -- cannot use range because copy by value
for i := 0; i < len(f.Fields); i++ {
ff := &f.Fields[i]
if ff.Name == vf.Name {
if vf.ErrorType != util.NOERROR {
ff.ErrorType = vf.ErrorType
ff.ErrorMsg = vf.ErrorMsg
}
}
}
}
}
func NewView(i interface{}, exclude ...string) *View {
var view View
nv := reflect.NewValue(i)
v := indirect(nv)
t := v.Type()
view.Name = t.Name()
for i := 0; i < v.NumField(); i++ {
f := t.Field(i)
if !ShouldExclude(f.Name, exclude...) {
fv := v.Field(i)
field := handleField(fv, f)
field.RenderType = RT_text //default
view.Fields = append(view.Fields, field)
}
}
return &view
}
func (f *Field) renderText() string {
name := strings.ToLower(f.Name)
//text := "\t\t<span class=\"field\">"
text := "\t\t<label for=" + name + ">" + f.Name + ":</label>"
text += "<span id=" + name + " >" + f.Value + "</span>\n\n"
//text += "\t\t</span>"
return text
}
func (f *Field) render() string {
doc := ""
switch f.RenderType {
case RT_input:
doc = f.renderInput()
case RT_select:
doc = f.renderSelect()
case RT_text:
doc = f.renderText()
case RT_textarea:
doc = f.renderTextarea()
case RT_hidden:
doc = f.renderHidden()
}
return doc
}
func (v *View) Render() string {
doc := "<div id=ui.doc." + v.Name + " class=show" + ">\n <fieldset>\n\t<legend>Details</legend>\n\n"
for _, field := range v.Fields {
doc += field.render()
}
doc += " </fieldset>\n <fieldset>\n\t<button type=submit>Edit</button>\n </fieldset>\n"
doc += "</div>"
return doc
}
func (f *Form) SetRenderType(fn string, rt int, fop FieldAttr) {
for i, v := range f.Fields {
if v.Name == fn {
f.Fields[i].RenderType = rt
f.Fields[i].FieldAttr = fop
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment