Created
May 18, 2011 11:32
-
-
Save anonymous/978407 to your computer and use it in GitHub Desktop.
render form
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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