├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── README_zh-CN.md ├── common ├── filesystem.go ├── html.go ├── tplfunc.go └── types.go ├── config ├── config.go ├── element.go ├── element_choice.go ├── element_interface.go ├── element_list_group.go ├── element_list_group_test.go ├── language.go └── utils.go ├── defaults ├── templates.go └── templates │ ├── allfields.html │ ├── base │ ├── button.html │ ├── datetime │ │ ├── date.html │ │ ├── datetime.html │ │ └── time.html │ ├── fieldset.html │ ├── fieldset_buttons.html │ ├── generic.html │ ├── input.html │ ├── langset.html │ ├── number │ │ ├── number.html │ │ └── range.html │ ├── options │ │ ├── checkbox.html │ │ ├── radiobutton.html │ │ └── select.html │ ├── static.html │ └── text │ │ ├── passwordinput.html │ │ ├── textareainput.html │ │ └── textinput.html │ ├── baseform.html │ ├── bootstrap3 │ ├── button.html │ ├── datetime │ │ ├── date.html │ │ ├── datetime.html │ │ └── time.html │ ├── fieldset.html │ ├── fieldset_buttons.html │ ├── generic.html │ ├── input.html │ ├── langset.html │ ├── number │ │ ├── number.html │ │ └── range.html │ ├── options │ │ ├── checkbox.html │ │ ├── radiobutton.html │ │ └── select.html │ ├── static.html │ └── text │ │ ├── passwordinput.html │ │ ├── textareainput.html │ │ └── textinput.html │ └── bootstrapform.html ├── example ├── forms.json ├── main.go └── run.bat ├── fields ├── button.go ├── datetime.go ├── field.go ├── field_interface.go ├── field_test.go ├── number.go ├── options.go ├── static.go └── text.go ├── fieldset.go ├── forms.go ├── forms_marshal.go ├── forms_test.go ├── go.mod ├── go.sum ├── json.go ├── langset.go ├── utils.go ├── validate.go └── widgets └── widgets.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.log 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | script: "go build -v" 3 | go: 4 | - 1.16 5 | - tip -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 coscms 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | forms 2 | ========== 3 | 4 | [](http://godoc.org/github.com/coscms/forms) 5 | 6 | Description 7 | =========== 8 | 9 | `forms` makes form creation and handling easy. It allows the creation of form without having to write HTML code or bother to make the code Bootstrap compatible. 10 | You can just create your form instance and add / populate / customize fields based on your needs. Or you can let `forms` do that for you starting from any object instance. 11 | 12 | To integrate `forms` forms into your application simply pass the form object to the template and call its Render method. 13 | In your code: 14 | 15 | ```go 16 | tmpl.Execute(buf, map[string]interface{}{"form": form}) 17 | ``` 18 | 19 | In your template: 20 | 21 | ```html 22 | {{ if .form }}{{ .form.Render }}{{ end }} 23 | ``` 24 | 25 | Installation 26 | ============ 27 | 28 | To install this package simply: 29 | 30 | ```go 31 | go get github.com/coscms/forms 32 | ``` 33 | 34 | Forms 35 | ===== 36 | 37 | There are two predefined themes for forms: base HTML forms and Bootstrap forms: they have different structures and predefined classes. 38 | Style aside, forms can be created from scratch or starting from a base instance. 39 | 40 | From scratch 41 | ------------ 42 | 43 | You can create a form instance by simply deciding its theme and providing its method and action: 44 | 45 | ```go 46 | form := NewWithConfig(&config.Config{ 47 | Theme:"base", 48 | Method:POST, 49 | Action:"/action.html", 50 | }) 51 | ``` 52 | 53 | Now that you have a form instance you can customize it by adding classes, parameters, CSS values or id. Each method returns a pointer to the same form, so multiple calls can be chained: 54 | 55 | ```go 56 | form.SetID("TestForm").AddClass("form").AddCSS("border", "auto") 57 | ``` 58 | 59 | Obviously, elements can be added as well: 60 | 61 | ```go 62 | form.Elements(fields.TextField("text_field")) 63 | ``` 64 | 65 | Elements can be either FieldSets or Fields: the formers are simply collections of fields translated into a `
` element. 66 | Elements are added in order, and they are displayed in the exact same order. Note that single elements can be removed from a form referencing them by name: 67 | 68 | ```go 69 | form.RemoveElement("text_field") 70 | ``` 71 | 72 | Typical usage looks like this: 73 | 74 | ```go 75 | form := NewWithConfig(&config.Config{ 76 | Theme:"base", 77 | Method:POST, 78 | Action:"/action.html", 79 | }).Elements( 80 | fields.TextField("text_field").SetLabel("Username"), 81 | FieldSet("psw_fieldset", 82 | fields.PasswordField("psw1").AddClass("password_class").SetLabel("Password 1"), 83 | fields.PasswordField("psw2").AddClass("password_class").SetLabel("Password 2"), 84 | ), 85 | fields.SubmitButton("btn1", "Submit"), 86 | ) 87 | ``` 88 | 89 | validation: 90 | 91 | ```go 92 | type User struct { 93 | Username string `valid:"Required;AlphaDash;MaxSize(30)"` 94 | Password1 string `valid:"Required"` 95 | Password2 string `valid:"Required"` 96 | } 97 | u := &User{} 98 | form.SetModel(u) //Must set model 99 | err := form.valid() 100 | if err != nil { 101 | // validation does not pass 102 | } 103 | ``` 104 | 105 | Details about validation, please visit: https://github.com/webx-top/validation 106 | 107 | A call to `form.Render()` returns the following form: 108 | 109 | ```html 110 | 121 | ``` 122 | 123 | From model instance 124 | ------------------- 125 | 126 | Instead of manually creating a form, it can be automatically created from an existing model instance: the package will try to infer the field types based on the instance fields and fill them accordingly. 127 | Default type-to-field mapping is as follows: 128 | 129 | * string: TextField 130 | * bool: Checkbox 131 | * time.Time: DatetimeField 132 | * int: NumberField 133 | * struct: recursively parse 134 | 135 | You can customize field behaviors by adding tags to instance fields. 136 | Without tags this code: 137 | 138 | ```go 139 | type User struct { 140 | Username string 141 | Password1 string 142 | Password2 string 143 | } 144 | u := &User{} 145 | form := NewFromModel(u, &config.Config{ 146 | Theme:"bootstrap3", 147 | Method:POST, 148 | Action:"/action.html", 149 | }) 150 | ``` 151 | 152 | validation: 153 | 154 | ```go 155 | err := form.valid() 156 | if err != nil { 157 | // validation does not pass 158 | } 159 | form.Render() 160 | ``` 161 | 162 | would yield this HTML form: 163 | 164 | ```html 165 | 174 | ``` 175 | 176 | A submit button is added by default. 177 | 178 | Notice that the form is still editable and fields can be added, modified or removed like before. 179 | 180 | When creating a form from a model instance, field names are created by appending the field name to the baseline; the baseline is empty for single level structs but is crafted when nested structs are found: in this case it becomes the field name followed by a dot. 181 | So for example, if the struct is: 182 | 183 | ```go 184 | type A struct { 185 | field1 int 186 | field2 int 187 | } 188 | type B struct { 189 | field0 int 190 | struct1 A 191 | } 192 | ``` 193 | 194 | The final form will contain fields "field0", "struct1.field1" and "struct1.field2". 195 | 196 | Tags 197 | ---- 198 | 199 | Struct tags can be used to slightly modify automatic form creation. In particular the following tags are parsed: 200 | 201 | * form_options: can contain the following keywords separated by Semicolon (;) 202 | 203 | - -: skip field, do not convert to HTML field 204 | - checked: for Checkbox fields, check by default 205 | - multiple: for select fields, allows multiple choices 206 | 207 | * form_widget: override custom widget with one of the following 208 | 209 | - text 210 | - hidden 211 | - textarea 212 | - password 213 | - select 214 | - datetime 215 | - date 216 | - time 217 | - number 218 | - range 219 | - radio 220 | - checkbox 221 | - static (simple text) 222 | 223 | * form_fieldset: define fieldset name 224 | * form_sort: sort number (asc, 0 ~ total-1) 225 | * form_choices: defines options for select and radio input fields 226 | - radio/checkbox example(format: id|value): 1|Option One|2|Option 2|3|Option 3 227 | - select example(format: group|id|value): G1|A|Option A|G1|B|Option B 228 | - "" group is the default one and does not trigger a `` rendering. 229 | * form_max: max value (number, range, datetime, date and time fields) 230 | * form_min: min value (number, range, datetime, date and time fields) 231 | * form_step: step value (range field) 232 | * form_rows: number of rows (textarea field) 233 | * form_cols: number of columns (textarea field) 234 | * form_value: input field value (used if field is empty) 235 | * form_label: label for input field 236 | 237 | The code would therefore be better like this: 238 | 239 | ```go 240 | type User struct { 241 | Username string 242 | Password1 string `form_widget:"password" form_label:"Password 1"` 243 | Password2 string `form_widget:"password" form_label:"Password 2"` 244 | SkipThis int `form_options:"-"` 245 | } 246 | u := User{} 247 | form := NewFromModel(u, &config.Config{ 248 | Theme:"bootstrap3", 249 | Method:POST, 250 | Action:"/action.html", 251 | }) 252 | form.Render() 253 | ``` 254 | 255 | which translates into: 256 | 257 | ```html 258 | 267 | ``` 268 | 269 | Fields 270 | ====== 271 | 272 | Field objects in `forms` implement the `fields.FieldInterface` which exposes methods to edit classes, parameters, tags and CSS styles. 273 | See the [documentation](http://godoc.org/github.com/coscms/forms) for details. 274 | 275 | Most of the field widgets have already been created and integrate with Bootstrap. It is possible, however, to define custom widgets to render fields by simply assigning an object implementing the widgets.WidgetInterface to the Widget field. 276 | 277 | Also, error messages can be added to fields via the `AddError(err)` method: in a Bootstrap environment they will be correctly rendered. 278 | 279 | Text fields 280 | ----------- 281 | 282 | This category includes text, password, textarea and hidden fields. They are all instantiated by providing the name, except the TextAreaField which also requires a dimension in terms of rows and columns. 283 | 284 | ```go 285 | f0 := fields.TextField("text") 286 | f1 := fields.PasswordField("password") 287 | f2 := fields.HiddenField("hidden") 288 | f3 := fields.TextAreaField("textarea", 30, 50) 289 | ``` 290 | 291 | Option fields 292 | ------------- 293 | 294 | This category includes checkbox, select and radio button fields. 295 | Checkbox field requires a name and a set of options to populate the field. The options are just a set of InputChoice (ID-Value pairs) objects: 296 | 297 | ```go 298 | opts := []fields.InputChoice{ 299 | fields.InputChoice{ID:"A", Val:"Option A"}, 300 | fields.InputChoice{ID:"B", Val:"Option B"}, 301 | } 302 | f := fields.CheckboxField("checkbox", opts) 303 | f.AddSelected("A", "B") 304 | ``` 305 | 306 | Radio buttons, instead, require a name and a set of options to populate the field. The options are just a set of InputChoice (ID-Value pairs) objects: 307 | 308 | ```go 309 | opts := []fields.InputChoice{ 310 | fields.InputChoice{ID:"A", Val:"Option A"}, 311 | fields.InputChoice{ID:"B", Val:"Option B"}, 312 | } 313 | f := fields.RadioField("radio", opts) 314 | ``` 315 | 316 | Select fields, on the other hand, allow option grouping. This can be achieved by passing a `map[string][]InputChoice` in which keys are groups containing choices given as values; the default (empty) group is "", which is not translated into any `` element. 317 | 318 | ```go 319 | opts := map[string][]fields.InputChoice{ 320 | "": []fields.InputChoice{fields.InputChoice{"A", "Option A"}}, 321 | "group1": []fields.InputChoice{ 322 | fields.InputChoice{ID:"B", Val:"Option B"}, 323 | fields.InputChoice{ID:"C", Val:"Option C"}, 324 | } 325 | } 326 | f := fields.SelectField("select", opts) 327 | ``` 328 | 329 | Select fields can allow multiple choices. To enable this option simply call the `MultipleChoice()` method on the field and provide the selected choices via `AddSelected(...string)`: 330 | 331 | ```go 332 | f.MultipleChoice() 333 | f.AddSelected("A", "B") 334 | ``` 335 | 336 | Number fields 337 | ------------- 338 | 339 | Number and range fields are included. 340 | Number field only require a name to be instantiated; minimum and maximum values can optionally be set by adding `min` and `max` parameters respectively. 341 | 342 | ```go 343 | f := fields.NumberField("number") 344 | f.SetParam("min", "1") 345 | ``` 346 | 347 | Range fields, on the other hand, require both minimum and maximum values (plus the identifier). The optional "step" value is set via `SetParam`. 348 | 349 | ```go 350 | f := fields.RangeField("range", 1, 10) 351 | f.SetParam("step", "2") 352 | ``` 353 | 354 | Datetime fields 355 | --------------- 356 | 357 | Datetime, date and time input fields are defined in `forms`. 358 | 359 | ```go 360 | f0 := fields.DatetimeField("datetime") 361 | f1 := fields.DateField("date") 362 | f2 := fields.TimeField("time") 363 | ``` 364 | 365 | Values can be set via `SetValue` method; there's no input validation but format strings are provided to ensure the correct time-to-string conversion. 366 | 367 | ```go 368 | t := time.Now() 369 | f0.SetValue(t.Format(fields.DATETIME_FORMAT)) 370 | f1.SetValue(t.Format(fields.DATE_FORMAT)) 371 | f2.SetValue(t.Format(fields.TIME_FORMAT)) 372 | ``` 373 | 374 | Buttons 375 | ------- 376 | 377 | Buttons can be created calling either the `Button`, `SubmitButton` or `ResetButton` constructor methods and providing a text identifier and the content of the button itself. 378 | 379 | ```go 380 | btn0 := fields.Button("btn", "Click me!") 381 | ``` 382 | 383 | License 384 | ======= 385 | 386 | `forms` is released under the MIT license. See [LICENSE](https://github.com/coscms/forms/blob/master/LICENSE). -------------------------------------------------------------------------------- /README_zh-CN.md: -------------------------------------------------------------------------------- 1 | forms 2 | ========== 3 | 4 | [](http://godoc.org/github.com/coscms/forms) 5 | 6 | 7 | 简介 8 | =========== 9 | 10 | 用`forms`可以方便的创建HTML表单,支持指定表单模板,在使用时您不用写HTML代码就可以便捷的创建出符合个人需求的表单。你可以根据自身需要创建一个表单实例然后添加、填充、自定义表单字段。 11 | 12 | `forms`可以很容易的集成到你的应用中,只需要通过调用form对象的Render方法即可渲染出表单的HTML代码: 13 | 14 | tmpl.Execute(buf, map[string]interface{}{"form": form}) 15 | 16 | 在模板中使用: 17 | 18 | {{ if .form }}{{ .form.Render }}{{ end }} 19 | 20 | 21 | 安装方式 22 | ============ 23 | 24 | 使用以下代码安装forms包: 25 | 26 | go get github.com/coscms/forms 27 | 28 | 表单模板 29 | ===== 30 | 31 | 本包中已经内置了两套表单模板:base和bootstrap3。它们代表两种不同的风格。除此之外,您也可以创建自己的表单模板。 32 | 33 | 入门指引 34 | ------------ 35 | 36 | 创建一个form实例,并指定表单模板为base,表单的提交方式为POST,表单的提交网址为“/action.html”: 37 | 38 | form := NewForm("base", POST, "/action.html") 39 | 40 | 41 | 现在,我们可以通过form实例来自定义表单的属性。form实例中的每一个方法都会返回form指针,因此你可以非常方便的采用链式调用来多次执行方法。下面,我们来定义HTML标签` 97 | 98 | 从model实例创建表单 99 | ------------------- 100 | 101 | 我们可以通过model实例来自动创建表单,免除了手动一个个添加表单字段的麻烦。本forms包会根据model实例中的属性字段及其类型来依次填充到表单中作为表单字段。 102 | model实例内属性字段类型与表单字段对应关系如下: 103 | 104 | * string: TextField (文本输入框) 105 | * bool: Checkbox (复选框) 106 | * time.Time: DatetimeField (日期输入框) 107 | * int: NumberField (数字输入框) 108 | * struct: 递归解析 109 | 110 | 也可以通过添加tag到model实例中的属性字段内来定义表单字段的类型。 111 | 不带tag的代码: 112 | 113 | type User struct { 114 | Username string 115 | Password1 string 116 | Password2 string 117 | } 118 | 119 | u := &User{} 120 | 121 | form := NewFormFromModel(u, "bootstrap3", POST, "/action.html") 122 | 123 | 验证表单数据: 124 | 125 | valid, passed := form.valid() 126 | if !passed { 127 | // validation does not pass 128 | } 129 | _ = valid 130 | 131 | form.Render() 132 | 133 | 生成的HTML代码如下: 134 | 135 | 144 | 145 | 默认会添加一个提交按钮。 146 | 147 | 注意:我们可以象前面介绍的那样添加、修改或删除某一个表单字段。 148 | 149 | When creating a form from a model instance, field names are created by appending the field name to the baseline; the baseline is empty for single level structs but is crafted when nested structs are found: in this case it becomes the field name followed by a dot. 150 | So for example, if the struct is: 151 | 152 | type A struct { 153 | field1 int 154 | field2 int 155 | } 156 | 157 | type B struct { 158 | field0 int 159 | struct1 A 160 | } 161 | 162 | The final form will contain fields "field0", "struct1.field1" and "struct1.field2". 163 | 164 | Tags 165 | ---- 166 | 167 | Struct tags can be used to slightly modify automatic form creation. 下面列出的这些tag会被解析: 168 | 169 | * form_options: 可以包含如下关键词,同时使用多个关键词时,用分号(;)隔开 170 | - -: 跳过此字段, 不转为HTML表单字段 171 | - checked: 针对Checkbox,默认选中 172 | - multiple: 指定select为允许多选 173 | * form_widget: 指定表单部件类型。支持以下类型: 174 | - text 175 | - hidden 176 | - textarea 177 | - password 178 | - select 179 | - datetime 180 | - date 181 | - time 182 | - number 183 | - range 184 | - radio 185 | - checkbox 186 | - static (简单的静态文本) 187 | * form_fieldset: 定义fieldset标题文字 188 | * form_sort: 排序编号 (按升序排列, 编号从0开始,范围为0 ~ 总数-1) 189 | * form_choices: select或radio输入字段的选项 190 | - radio/checkbox 范例(格式: id|value): 1|选项一|2|选项二|3|选项三 191 | - select 范例(格式: group|id|value): 组1|A|选项A|组1|B|选项B 192 | - "" 组名为空白时,默认将不渲染``。 193 | * form_max: 允许的最大值 (用于number、range、datetime、date 和 time 类型输入框) 194 | * form_min: 允许的最小值 (用于number、range、datetime、date 和 time 类型输入框) 195 | * form_step: 步进值 (用于range输入字段) 196 | * form_rows: 行数 (用于textarea) 197 | * form_cols: 列数 (用于textarea) 198 | * form_value: 输入字段的默认值 199 | * form_label: label内容 200 | 201 | 例如: 202 | 203 | type User struct { 204 | Username string 205 | Password1 string `form_widget:"password" form_label:"Password 1"` 206 | Password2 string `form_widget:"password" form_label:"Password 2"` 207 | SkipThis int `form_options:"-"` 208 | } 209 | 210 | u := User{} 211 | 212 | form := NewFormFromModel(u, "bootstrap3", POST, "/action.html") 213 | form.Render() 214 | 215 | 它们最后会翻译成以下代码: 216 | 217 | 226 | 227 | Fields 228 | ====== 229 | 230 | Field objects in `forms` implement the `fields.FieldInterface` which exposes methods to edit classes, parameters, tags and CSS styles. 231 | See the [documentation](http://godoc.org/github.com/coscms/forms) for details. 232 | 233 | Most of the field widgets have already been created and integrate with Bootstrap. It is possible, however, to define custom widgets to render fields by simply assigning an object implementing the widgets.WidgetInterface to the Widget field. 234 | 235 | Also, error messages can be added to fields via the `AddError(err)` method: in a Bootstrap environment they will be correctly rendered. 236 | 237 | Text fields 238 | ----------- 239 | 240 | This category includes text, password, textarea and hidden fields. They are all instantiated by providing the name, except the TextAreaField which also requires a dimension in terms of rows and columns. 241 | 242 | f0 := fields.TextField("text") 243 | f1 := fields.PasswordField("password") 244 | f2 := fields.HiddenField("hidden") 245 | f3 := fields.TextAreaField("textarea", 30, 50) 246 | 247 | Option fields 248 | ------------- 249 | 250 | This category includes checkbox, select and radio button fields. 251 | Checkbox field requires a name and a set of options to populate the field. The options are just a set of InputChoice (ID-Value pairs) objects: 252 | 253 | opts := []fields.InputChoice{ 254 | fields.InputChoice{ID:"A", Val:"Option A"}, 255 | fields.InputChoice{ID:"B", Val:"Option B"}, 256 | } 257 | f := fields.CheckboxField("checkbox", opts) 258 | f.AddSelected("A", "B") 259 | 260 | Radio buttons, instead, require a name and a set of options to populate the field. The options are just a set of InputChoice (ID-Value pairs) objects: 261 | 262 | opts := []fields.InputChoice{ 263 | fields.InputChoice{ID:"A", Val:"Option A"}, 264 | fields.InputChoice{ID:"B", Val:"Option B"}, 265 | } 266 | f := fields.RadioField("radio", opts) 267 | 268 | Select fields, on the other hand, allow option grouping. This can be achieved by passing a `map[string][]InputChoice` in which keys are groups containing choices given as values; the default (empty) group is "", which is not translated into any `` element. 269 | 270 | opts := map[string][]fields.InputChoice{ 271 | "": []fields.InputChoice{fields.InputChoice{"A", "Option A"}}, 272 | "group1": []fields.InputChoice{ 273 | fields.InputChoice{ID:"B", Val:"Option B"}, 274 | fields.InputChoice{ID:"C", Val:"Option C"}, 275 | } 276 | } 277 | f := fields.SelectField("select", opts) 278 | 279 | Select fields can allow multiple choices. To enable this option simply call the `MultipleChoice()` method on the field and provide the selected choices via `AddSelected(...string)`: 280 | 281 | f.MultipleChoice() 282 | f.AddSelected("A", "B") 283 | 284 | Number fields 285 | ------------- 286 | 287 | Number and range fields are included. 288 | Number field only require a name to be instantiated; minimum and maximum values can optionally be set by adding `min` and `max` parameters respectively. 289 | 290 | f := fields.NumberField("number") 291 | f.SetParam("min", "1") 292 | 293 | Range fields, on the other hand, require both minimum and maximum values (plus the identifier). The optional "step" value is set via `SetParam`. 294 | 295 | f := fields.RangeField("range", 1, 10) 296 | f.SetParam("step", "2") 297 | 298 | 299 | Datetime fields 300 | --------------- 301 | 302 | Datetime, date and time input fields are defined in `go-form-it`. 303 | 304 | f0 := fields.DatetimeField("datetime") 305 | f1 := fields.DateField("date") 306 | f2 := fields.TimeField("time") 307 | 308 | Values can be set via `SetValue` method; there's no input validation but format strings are provided to ensure the correct time-to-string conversion. 309 | 310 | t := time.Now() 311 | f0.SetValue(t.Format(fields.DATETIME_FORMAT)) 312 | f1.SetValue(t.Format(fields.DATE_FORMAT)) 313 | f2.SetValue(t.Format(fields.TIME_FORMAT)) 314 | 315 | Buttons 316 | ------- 317 | 318 | Buttons can be created calling either the `Button`, `SubmitButton` or `ResetButton` constructor methods and providing a text identifier and the content of the button itself. 319 | 320 | btn0 := fields.Button("btn", "Click me!") 321 | 322 | 323 | License 324 | ======= 325 | 326 | `forms` is released under the MIT license. See [LICENSE](https://github.com/coscms/forms/blob/master/LICENSE). -------------------------------------------------------------------------------- /common/filesystem.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "errors" 5 | "io/fs" 6 | ) 7 | 8 | var FileSystem FileSystems 9 | 10 | type ( 11 | FileSystems []fs.FS 12 | ) 13 | 14 | func (f FileSystems) Open(name string) (file fs.File, err error) { 15 | for _, i := range f { 16 | file, err = i.Open(name) 17 | if err == nil || !errors.Is(err, fs.ErrNotExist) { 18 | return 19 | } 20 | } 21 | return 22 | } 23 | 24 | func (f FileSystems) Size() int { 25 | return len(f) 26 | } 27 | 28 | func (f FileSystems) IsEmpty() bool { 29 | return f.Size() == 0 30 | } 31 | 32 | func (f *FileSystems) Register(fileSystem fs.FS) { 33 | *f = append(*f, fileSystem) 34 | } 35 | -------------------------------------------------------------------------------- /common/html.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "html/template" 5 | "strings" 6 | 7 | "github.com/webx-top/com" 8 | ) 9 | 10 | type ( 11 | HTMLAttrValues []string 12 | HTMLAttributes map[template.HTMLAttr]interface{} 13 | HTMLData map[string]interface{} 14 | ) 15 | 16 | func (s HTMLAttrValues) String() string { 17 | return strings.Join([]string(s), ` `) 18 | } 19 | 20 | func (s HTMLAttrValues) IsEmpty() bool { 21 | return len(s) == 0 22 | } 23 | 24 | func (s HTMLAttrValues) Size() int { 25 | return len(s) 26 | } 27 | 28 | func (s HTMLAttrValues) Exists(attr string) bool { 29 | return com.InSlice(attr, s) 30 | } 31 | 32 | func (s *HTMLAttrValues) Add(value string) { 33 | (*s) = append((*s), value) 34 | } 35 | 36 | func (s *HTMLAttrValues) Remove(value string) { 37 | ind := -1 38 | for i, v := range *s { 39 | if v == value { 40 | ind = i 41 | break 42 | } 43 | } 44 | 45 | if ind != -1 { 46 | *s = append((*s)[:ind], (*s)[ind+1:]...) 47 | } 48 | } 49 | 50 | func (s HTMLAttributes) Exists(attr string) bool { 51 | _, ok := s[template.HTMLAttr(attr)] 52 | return ok 53 | } 54 | 55 | func (s HTMLAttributes) FillFrom(data map[string]interface{}) { 56 | for k, v := range data { 57 | s[template.HTMLAttr(k)] = v 58 | } 59 | } 60 | 61 | func (s HTMLAttributes) FillFromStringMap(data map[string]string) { 62 | for k, v := range data { 63 | s[template.HTMLAttr(k)] = v 64 | } 65 | } 66 | 67 | func (s HTMLData) Exists(key string) bool { 68 | _, ok := s[key] 69 | return ok 70 | } 71 | -------------------------------------------------------------------------------- /common/tplfunc.go: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2016-present Wenhui Shen{{.text}}
6 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/base/text/passwordinput.html: -------------------------------------------------------------------------------- 1 | {{- define "main" }} 2 | {{- if .label }} 3 | 4 | {{- end }} 5 | 6 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/base/text/textareainput.html: -------------------------------------------------------------------------------- 1 | {{- define "main" }} 2 | {{- if .label }} 3 | 4 | {{- end }} 5 | 7 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/base/text/textinput.html: -------------------------------------------------------------------------------- 1 | {{- define "main" }} 2 | {{- if .label }} 3 | 4 | {{- end }} 5 | 6 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/baseform.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /defaults/templates/bootstrap3/button.html: -------------------------------------------------------------------------------- 1 | {{- define "main" }} 2 | 3 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/bootstrap3/datetime/date.html: -------------------------------------------------------------------------------- 1 | {{- define "main" }} 2 | {{- template "generic" . }} 3 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/bootstrap3/datetime/datetime.html: -------------------------------------------------------------------------------- 1 | {{- define "main" }} 2 | {{- template "generic" . }} 3 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/bootstrap3/datetime/time.html: -------------------------------------------------------------------------------- 1 | {{- define "main" }} 2 | {{- template "generic" . }} 3 | {{- end }} -------------------------------------------------------------------------------- /defaults/templates/bootstrap3/fieldset.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /defaults/templates/bootstrap3/fieldset_buttons.html: -------------------------------------------------------------------------------- 1 |{{.text}}
7 |