-
Notifications
You must be signed in to change notification settings - Fork 6
/
list_select.go
206 lines (180 loc) · 5.74 KB
/
list_select.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package control
import (
"bytes"
"context"
"github.com/goradd/gengen/pkg/maps"
"github.com/goradd/goradd/pkg/html"
"github.com/goradd/goradd/pkg/page"
"github.com/goradd/goradd/pkg/page/control/data"
"reflect"
)
type SelectListI interface {
page.ControlI
ItemListI
}
// SelectList is typically a dropdown list with a single selection. Items are selected by id number, and the SelectList
// completely controls the ids in the list. Create the list by calling AddItem or AddItems to add ListItemI objects.
// Or, use the embedded DataManager to load items. Set the size attribute if you want to display it as a
// scrolling list rather than a dropdown list.
type SelectList struct {
page.Control
ItemList
data.DataManager
selectedId string
}
// NewSelectList creates a new select list
func NewSelectList(parent page.ControlI, id string) *SelectList {
t := &SelectList{}
t.Init(t, parent, id)
return t
}
// Init is called by subclasses.
func (l *SelectList) Init(self page.ControlI, parent page.ControlI, id string) {
l.Control.Init(self, parent, id)
l.ItemList = NewItemList(l)
l.Tag = "select"
}
// Validate is called by the framework to validate the contents of the control. For a SelectList,
// this is typically just checking to see if something was selected if a selection is required.
func (l *SelectList) Validate(ctx context.Context) bool {
if v := l.Control.Validate(ctx); !v {
return false
}
if l.IsRequired() && l.SelectedItem().IsEmptyValue() {
if l.ErrorForRequired == "" {
l.SetValidationError(l.ΩT("A selection is required"))
} else {
l.SetValidationError(l.ErrorForRequired)
}
return false
}
return true
}
// ΩUpdateFormValues is an internal function that lets us reflect the value of the selection that is currently on the browser
func (l *SelectList) ΩUpdateFormValues(ctx *page.Context) {
id := l.ID()
if v, ok := ctx.FormValue(id); ok {
l.selectedId = v
}
}
// SelectedItem will return the currently selected item. If no item has been selected, it will return the first item
// in the list, since that is what will be showing in the selection list, and will update its internal pointer to
// make the first item the current selection.
func (l *SelectList) SelectedItem() ListItemI {
if l.selectedId == "" {
if l.Len() == 0 {
return nil
} else {
l.selectedId = l.items[0].ID()
return l.items[0]
}
}
return l.GetItem(l.selectedId)
}
// SetSelectedId sets the current selection to the given id. You must ensure that the item with the id exists, it will
// not attempt to make sure the item exists.
func (l *SelectList) SetSelectedID(id string) {
l.selectedId = id
l.AddRenderScript("val", id)
}
// Value implements the Valuer interface for general purpose value getting and setting
func (l *SelectList) Value() interface{} {
if i := l.SelectedItem(); i == nil {
return nil
} else {
return i.Value()
}
}
// SetValue implements the Valuer interface for general purpose value getting and setting
func (l *SelectList) SetValue(v interface{}) {
id, _ := l.GetItemByValue(v)
l.SetSelectedID(id)
}
// IntValue returns the select value as an integer.
func (l *SelectList) IntValue() int {
if i := l.SelectedItem(); i == nil {
return 0
} else {
return i.IntValue()
}
}
// StringValue returns the selected value as a string
func (l *SelectList) StringValue() string {
if i := l.SelectedItem(); i == nil {
return ""
} else {
return i.StringValue()
}
}
// SelectedLabel returns the label of the selected item
func (l *SelectList) SelectedLabel() string {
item := l.SelectedItem()
if item != nil {
return item.Label()
}
return ""
}
// ΩMarshalState is an internal function to save the state of the control
func (l *SelectList) ΩMarshalState(m maps.Setter) {
m.Set("sel", l.selectedId)
}
// ΩUnmarshalState is an internal function to restore the state of the control
func (l *SelectList) ΩUnmarshalState(m maps.Loader) {
if v,ok := m.Load("sel"); ok {
if s, ok := v.(string); ok {
l.selectedId = s
}
}
}
// ΩDrawingAttributes retrieves the tag's attributes at draw time. You should not normally need to call this, and the
// attributes are disposed of after drawing, so they are essentially read-only.
func (l *SelectList) ΩDrawingAttributes() *html.Attributes {
a := l.Control.ΩDrawingAttributes()
a.SetDataAttribute("grctl", "selectlist")
a.Set("name", l.ID()) // needed for posts
if l.IsRequired() {
a.Set("required", "")
}
return a
}
// ΩDrawInnerHtml is called by the framework during drawing of the control to draw the inner html of the control
func (l *SelectList) ΩDrawInnerHtml(ctx context.Context, buf *bytes.Buffer) (err error) {
if l.HasDataProvider() {
l.GetData(ctx, l)
}
h := l.getItemsHtml(l.items)
buf.WriteString(h)
return nil
}
func (l *SelectList) getItemsHtml(items []ListItemI) string {
var h = ""
for _, item := range items {
if item.HasChildItems() {
tag := "optgroup"
innerhtml := l.getItemsHtml(item.ListItems())
attributes := item.Attributes().Copy()
attributes.Set("label", item.Label())
h += html.RenderTag(tag, attributes, innerhtml)
} else {
attributes := item.Attributes().Copy()
attributes.Set("value", item.ID())
if l.selectedId == item.ID() {
attributes.Set("selected", "")
}
h += html.RenderTag("option", attributes, item.RenderLabel())
}
}
return h
}
// SetData overrides the default data setter to add objects to the item list.
// The result is kept in memory currently.
// ItemLister, ItemIDer, Labeler or Stringer types. This function can accept one or more lists of items, or
// single items.
func (l *SelectList) SetData(data interface{}) {
kind := reflect.TypeOf(data).Kind()
if !(kind == reflect.Slice || kind == reflect.Array) {
panic("you must call SetData with a slice or array")
}
l.ItemList.Clear()
l.AddListItems(data)
}