formhandler.md 13.8 KB
Newer Older
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
# $.formhandler

An interactive table component for [FormHandler][formhandler] data.

```html
<div class="formhandler" data-src="formhandler-url" data-page-size="10"></div>
<script>
$('.formhandler').formhandler({
  pageSize: 20
})
</script>
```

Options can passed via an options dict, and over-ridden using `data-` attributes.
In the above example, `data-page-size="10"` over-rides `pageSize: 20`.

[formhandler]: https://learn.gramener.com/guide/formhandler/

## $.formhandler options

The full list of options is below. Simple options can be specified as `data-` attributes as well.

- `src`: [FormHandler][formhandler] URL endpoint
- `data`: Array of objects. Dataset for formhandler table. If both `src` and `data` are provided, `data` takes priority.
- `namespace`: (Optional) If the URL has `?name:key=value`, the filter
  `key=value` only applies to formhandlers with namespace as `name`.
  Filters without a namespace like `?key=value` will apply to all formhandlers.
- `columns`: comma-separated column names to display, or a list of objects with these keys:
    - `name`: column name. `"*"` is a special column placeholder for "all columns" (options given for `"*"` are applied for all columns)
    - `title`: for header display. Defaults to the same value as `name`
    - `type`: `text` (default) / `number` / `date`. Data type. Determines filters to be used
    - `format`: string / function that returns formatted cell display value.
S Anand's avatar
S Anand committed
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
        - function accepts an object with these keys:
            - `name`: column name
            - `value`: cell data value
            - `row`: row data
            - `index`: row index
            - `data`: the dataset from `src`
        - strings specify a numeral.js format if the value is a number (you must include numeral.js)
        - strings specify a moment.js format if the value is a date (you must include moment.js)
    - `editable`: `true` (default) / `false`. When `true`, edit and save buttons appears at end of each row. To bind UI input element such as dropdown, datepicker, radio etc., `editable` accepts an object with these keys.
        - `input`: **Mandatory**. The type of input element to use. The valid values are checkbox, radio, range, select, and any other legal [HTML form input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input).
        - `options`: An array of options to select from. **Mandatory** if `input` is either of `select` or `radio`
        - `attrs`: To place common attributes such as max, min, placeholder, name etc., on the input.
          Example: `{placeholder: "Age", max:100}` renders `<input placeholder="Age" max="100">`
        - `validationMessage`: The message to be shown when invalid input is entered.
          Example: `"Age must be less than 100"`
48
    - `template`: string template / function that renders the cell.
S Anand's avatar
S Anand committed
49
50
51
52
53
54
55
56
57
        - function accepts an object with these keys:
            - `name`: column name
            - `value`: cell data value
            - `format`: formatted cell display value
            - `link`: cell link value (if applicable)
            - `index`: row index
            - `row`: row data
            - `data`: the dataset from `src`
        - string template can use the above variables
58
    - `sort`: `true` / `false` / operators dict with:
S Anand's avatar
S Anand committed
59
        - `{'': 'Sort ascending', '-': 'Sort descending'}` (default)
60
    - `filters`: `true` (default) / `false` / operators dict with:
S Anand's avatar
S Anand committed
61
        - `{'', 'Equals...', '!', 'Does not equal...', ...}`. The default list of operators is based on the auto-detected type of the column.
62
    - `link`: string / function that generates a link for this each cell.
S Anand's avatar
S Anand committed
63
        - If no `link:` is specified, clicking on the cell filters by that cell.
S Anand's avatar
WIP    
S Anand committed
64
        - If `link: false`, the cell has no link
S Anand's avatar
S Anand committed
65
66
67
68
69
70
71
72
73
        - If `link:` is a function, opens a new window with the URL as `fn(args)`.
            - function accepts an object with these keys:
                - `name`: column name
                - `value`: cell data value
                - `format`: formatted cell display value
                - `index`: row index
                - `row`: row data
                - `data`: the dataset from `src`
            - Example: `function(args) { return 'https://example.org/city/' + args.value }`
S Anand's avatar
WIP    
S Anand committed
74
75
76
77
78
            - Example: `function(args) { return false }` clears the link
        - If `link:` is a URL with a path, opens a new window with the string
          URL interpolated as a lodash template with an object (mentioned above)
          as data. Example: `"https://example.org/city/<%- value >"`
        - If `link:` is a URL with only query parameters, apply the filters. Example: `"?name=<%- row.name %>"`
79
80
81
82
    - `hideable`: `true` (default) / `false`. Show or hide `Hide` option in header dropdown
    - `hide`: `true` / `false` (default). Hides the column
    - `unique`: TODO: {dict of query parameter and display value} or [list of values] or function?
- `edit`: Shows the Edit control. Can be `true` / `false` (default). Can also pass an object.
S Anand's avatar
S Anand committed
83
    - `done`: function that gets called after saving the edited row.
84
- `add`: Show the Add control. Can be `true` / `false` (default). Can also pass an object.
S Anand's avatar
S Anand committed
85
    - `done`: function that gets called after saving the new row.
86
- `actions`: A list of objects. you need not add it to actions
S Anand's avatar
S Anand committed
87
88
89
90
91
92
93
94
    - `{{action}}`: a function() that gets triggered on clicking the element with `data-action='{{action}}` attribute. The value of `data-action` attribute must match with key `{{action}}` in `actions`.
        - function accepts an object with these keys:
            - `row`: row data
            - `index`: index of the row in the dataset from `src`
            - `notify(message)`: a function that shows a notification
        - If the return value can be a jQuery deferred (e.g. `$.ajax`), it shows a loading indicator and a success / failure message when the deferred is resolved. Example:
            - `highlight_row`: `function(obj) { $(obj.row).addClass('.yellow_color')}`. Either a new column can be defined in `columns:` (example: {`name`: `Additional Col`}) with cell_template having an element with data attribute as `data-action='highlight_row'` or can use an existing column but with custom template that has an element with data attribute as `data-action='highlight_row'`.
    - Note: DELETE operation is executed on a row if an element has data attribute `data-action='delete'`. If `delete` action is given in `actions`, the function given for `delete` is executed on click of an element with `data-action='delete'` instead od executing DELETE operation.
95
96
- `onhashchange`: `true` re-renders table on hashchange based on filters in URL
  hash. Set `false` to disable listening to hashchange (default `true`)
97
- `table`: Shows the table control. Can be:
S Anand's avatar
S Anand committed
98
99
100
    - `true`: displays a table (default)
    - `'grid'`: renders a grid instead of a table
    - `false`: disables the table (and shows nothing for the main content)
101
102
- `count`: Shows the number of rows. Can be `true` (default) / `false`
- `page`: Shows the page control. Can be `true` (default) / `false`.
S Anand's avatar
WIP    
S Anand committed
103
- `pageSize`: page size (or via `data-page-size`). Defaults to 100
104
- `size`: Shows the page size control. Can be `true` (default) / `false`
S Anand's avatar
WIP    
S Anand committed
105
- `sizeValues`: Allowed page size values (or via `data-size-values`). Defaults to `[10, 20, 50, 100, 500, 1000]`
106
- `export`: Shows the export control. Can be `true` (default) / `false`
S Anand's avatar
WIP    
S Anand committed
107
- `exportFormats`: Defines export formats to use (or via `data-export-formats`). E.g. `{xlsx: 'Excel', 'csv': 'CSV'}`
108
109
110
111
112
113
114
115
116
- `filters`: Shows the applied filters control. Can be `true` (default) / `false`
- `transform`: an optional function() that modifies data. It accepts a dict that has keys:
    - `data`: the FormHandler data
    - `meta`: the FormHandler metadata from the `FH-*` HTTP headers
    - `args`: the URL query parameters passed to the FormHandler
    - `options`: the options applicable to the FormHandler
    - returns a dict with modified values of `data` and `meta`
- `icon`: if `table: 'grid'` is used, display an icon. string / function that renders the cell.
    - function accepts an object with these keys:
S Anand's avatar
S Anand committed
117
118
119
120
121
122
123
        - `row`: row data
        - `data`: the dataset from `src`
        - `index`: index of the row in the dataset from `src`
    - Example:
        - `icon: 'fa fa-home fa-3x'` renders a FontAwesome home icon
        - `icon: './path/to/image.png'` renders the image specified
        - `icon: function(args) { return args.row['image_link'] }` renders an image with `src` attribute as the value from column name `image_link`
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

**Advanced**. Each component can have a target which specifies a selector. For
e.g., to render the export button somewhere else, use
`data-export-target=".navbar-export"`. This replaces the `.navbar-export`
contents with the export button. (It searches within the table container for
`.navbar-export` first, and if not found, searches everywhere.) Available
targets are:
- `tableTarget`
- `countTarget`
- `pageTarget`
- `sizeTarget`
- `exportTarget`
- `filtersTarget`
- `searchTarget`

**Advanced**: Each component's template string can be over-ridden. For example,
`data-search-template="<input type='search'>"` will replace the search template
with a simple input. Available template strings are:
- `tableTemplate`
S Anand's avatar
S Anand committed
143
- `table_gridTemplate`
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
- `countTemplate`
- `pageTemplate`
- `sizeTemplate`
- `exportTemplate`
- `filtersTemplate`
- `searchTemplate`
- `rowTemplate`, which can be a string template or function
  - function accepts an object with these keys:
    - `row`: row data
    - `index`: row index
    - `data`: the dataset from `src`
  - string template can use the above variables

Features to be implemented:

- Loading indicator
- Full text search
- URL targets other than '#', e.g. pushState

## $.formhandler events

- `load` is fired on the source when the template is rendered. Attributes:
    - `formdata`: the FormHandler data
    - `meta`: the FormHandler metadata
    - `args`: the URL query parameters passed to the request
    - `options`: applied options to the FormHandler

  Note: Make sure `load` event listener is attached before calling `$.formhandler()`

S Anand's avatar
S Anand committed
173
174
- `editmode` is fired on the source when the Edit button is clicked and table changes to edit mode.

175
176
## $.formhandler examples

177
### Render from a FormHandler
178
179
180
181
182
183
184
185

```html
<div class="formhandler" data-src="./data"></div>
<script>
  $('.formhandler').formhandler()
</script>
```

186
### Access data inside formhandler
187
188
189
190
191

```html
<div class="formhandler" data-src="./data"></div>
<script>
  $('.formhandler')
192
193
    .on('load', function(data, meta, args, options) {
      console.log('data inside formhandler table: ', data)
194
195
196
197
198
    })
    .formhandler()
</script>
```

199
## Draw chart in cell
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

```html
<div class="formhandler" data-src="./data"></div>
<script>
  $('.formhandler').formhandler({
    columns: [
      {name: '*'},
      {
        name: 'c1',
        format: function (o) {
          return '<svg height="10" width="10"><circle cx="5" cy="5" r="' + o.c1 / 10 + '" fill="red"></circle></svg>'
        }
      }
    }
  })
</script>
```
S Anand's avatar
S Anand committed
217

218
### Customize inputs in edit mode
S Anand's avatar
S Anand committed
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

```html
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker.css"/>

<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.js"></script>

<div class="edit-fh" data-src="./data"></div>
<script>
      $('.edit-fh').formhandler({
        columns: [
          {
            name: 'ID',
            editable: false     // Disable edit for column "ID" because it is a primary key and cannot be edited.
          },
          {
            name: 'Continent'  // Defaults to editable: false
          },
          {
            name: 'c1',
            editable: {
              input: 'number',
242
243
244
              // keys and values in `attrs` will be added as
              // <input type="number" min=10 max=100 placeholder="Age"/>
              attrs: {
S Anand's avatar
S Anand committed
245
246
                min: 10,
                max: 100,
247
248
249
                placeholder: 'Age'
              },
              validationMessage: 'Age must be between 0-100'
S Anand's avatar
S Anand committed
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
            }
          },
          {
            name: 'Stripes',
            editable: {
              input: 'select',  // renders a default select dropdown as <select class="form-control form-control-sm">...</select>
              options: [        // `options` is mandatory because `input` is "select"
                'Vertical',
                'Horizontal',
                'Diagonal'
              ]
            }
          },
          {
            name: 'Shapes',
            editable: {
              input: 'select',
              options: [
                'Circle',
                'Crescent',
                'Triangle',
                'Stars'
              ],
              attrs: {
                class: 'select-example-basic',  // To render the dropdown as select2 library dropdown, add class attribute as identifier
                name: 'shapes'
              }
            }
          },
          {
            name: 'date_col',
            editable: {
              input: 'text',
              attrs: { // To edit column "date_col" using a date picker widget using "bootstrap-datepicker" library, add class attribute as identifier
                class: 'datepicker-example form-control form-control-sm'
              }
            }
          }
        ]
      }).on('editmode', function () {
        // turns <select class="select-example-basic">...</select> to select2 dropdown widget
        $('.select-example-basic').select2()
        // turns <input type="text" class="datepicker-example"/> to bootstrap-datepicker calendar widget
        $('.datepicker-example').datepicker({
          format: 'dd-mm-yyyy',
          todayHighlight: true,
          autoclose: true
        })
      })
</script>
```