UNPKG

8.4 kBMarkdownView Raw
1# data-binding
2
3Components with bindings to data objects. These components, combined with the hooks from `@olenbetong/react-data-object-connect`, JSX can be written with a similar structure to Appframe data binding.
4
5## Getting started
6
7### Installation
8
9The library is published to NPM. To use, install from the command line
10
11```
12npm i @olenbetong/data-binding
13```
14
15Or use the IIFE build from unpkg.com
16
17```html
18<script
19 crossorigin
20 src="https://unpkg.com/@olenbetong/data-binding@latest/dist/iife/data-binding.min.js"
21 type="text/javascript"
22></script>
23```
24
25### Context
26
27Use the DataObjectProvider to provide data object context to components in the child tree.
28
29This is the equivalent of setting data-object-id on an element in Appframe data binding.
30
31```jsx
32import {
33 DataObjectProvider,
34 BoundInput
35} from "/file/component/modules/esm/data-binding.min.js";
36
37function MyComponent(props) {
38 return (
39 <DataObjectProvider dataObject={dsMyDataObject}>
40 <BoundInput type="text" field="MyField" />
41 </DataObjectProvider>
42 );
43}
44```
45
46### Components
47
48#### BoundInput
49
50Renders an input with a 2-way binding to a field in the data object passed by data object context.
51
52```jsx
53import {
54 DataObjectProvider,
55 BoundInput
56} from "/file/component/modules/esm/data-binding.min.js";
57
58function MyComponent(props) {
59 return (
60 <DataObjectProvider value={dsMyDataObject}>
61 <BoundInput type="text" field="MyField" />
62 </DataObjectProvider>
63 );
64}
65```
66
67#### BoundSelect
68
69Loads data from a data object with an optional filter, and displays a select element with the data. Doesn't use the data object directly, but uses a data handler. This means the select is independent from the data object state.
70
71There are two ways to set the options from the data object:
72
731. Pass a function to the `option` property. This should take the record as the first argument and return a React node (<option/>)
742. Set the `valueField` to the data object field that represents the value. Optionally set the `descriptionField` to another field that should be shown in the dropdown.
75
76If the `dataObject` property isn't set, the select will still be bound to the current data object context, but children have to be set manually. It is also possible to add options manually in addition to data object options.
77
78Other properties:
79
80- `dataObject` - Data object used to list the options
81- `filter` - Filter passed to the data object
82- `field` - Field to bind the value to
83- `nullable` - If true, will add an empty option (default: `true`)
84
85The components in this example will create the same select using the 2 different methods above:
86
87```jsx
88import { DataObjectProvider, BoundSelect } from "@olenbetong/data-binding";
89
90function MyComponentWithOptionFunc() {
91 return (
92 <BoundSelect
93 dataObject={dsSomeDataObject}
94 filter="IsAGoodRecord = 1"
95 option={record => <option value={record.Value}>{record.Name}</option>}
96 />
97 );
98}
99
100function MyComponentWithFields() {
101 return (
102 <BoundSelect
103 dataObject={dsSomeDataObject}
104 filter="IsAGoodRecord = 1"
105 valueField="Value"
106 descriptionField="Name"
107 />
108 );
109}
110
111function MyComponentWithUnboundOptions() {
112 return (
113 <BoundSelect field="SomeEnumerableField">
114 <option value="value1">Value 1</option>
115 <option value="value2">Value 2</option>
116 <option value="value3">Value 3</option>
117 </BoundSelect>
118 );
119}
120
121function MyComponentWithCombinedOptions() {
122 return (
123 <BoundSelect
124 field="SomeEnumerableField"
125 dataObject={dsSomeDataObject}
126 filter="IsAGoodRecord = 1"
127 valueField="Value"
128 descriptionField="Name"
129 >
130 <option value="value1">Value 1</option>
131 <option value="value2">Value 2</option>
132 <option value="value3">Value 3</option>
133 </BoundSelect>
134 );
135}
136```
137
138#### SaveButton
139
140Calls endEdit on the data object passed by data object context.
141
142Unlike save buttons in Appframe data binding, the button is not disabled if the record isn't dirty. This can be achieved using the useDirty hook from `@olenbetong/react-data-object-connect`. This also applies to the CancelButton.
143
144```jsx
145import { DataObjectProvider, SaveButton } from '/file/component/modules/esm/data-binding.min.js';
146
147function MyOuterComponent(props) {
148 return (
149 <DataObjectProvider value={dsMyDataObject}>
150 <MySaveButton />
151 </DataObjectProvider>
152 )
153}
154
155function MyComponent(props) {
156 const dataObject = useContext(DataObjectProvider);
157 const isDirty = useDirty(dataObject);
158
159 return (
160 <SaveButton disabled={!isDirty} className='btn btn-secondary'>
161 <i className='fa fa-save mr-2'/>
162 Save changes
163 </SaveButton>
164 )
165```
166
167#### CancelButton
168
169Calls cancelEdit on the data object passed by data object context.
170
171```jsx
172import { DataObjectProvider, CancelButton } from '/file/component/modules/esm/data-binding.min.js';
173
174function MyComponent(props) {
175 return (
176 <DataObjectProvider value={dsMyDataObject}>
177 <CancelButton className='btn btn-secondary'>
178 Cancel changes
179 </CancelButton>
180 </DataObjectProvider>
181 )
182```
183
184#### DeleteButton
185
186Deletes the current row of the data object passed by data object context. Takes a boolean confirm property to prompt the user to confirm the delete. The confirm prompt message can be customized with the prompt property.
187
188The `prompt` property can also be a function. When the button is clicked, the function will be called with the props passed to the delete button as the first argument. If the function returns `true` (not other trueish values), the record will be deleted. Alternatively, a promise can be returned. The record will be deleted if the promise resolves with `true`.
189
190The delete button also accepts an `index` property to be able to use it outside of the current row. E.g. when listing the records.
191
192```jsx
193import {
194 DataObjectProvider,
195 DeleteButton
196} from "/file/component/modules/esm/data-binding.min.js";
197
198function MyComponent(props) {
199 return (
200 <DataObjectProvider value={dsMyDataObject}>
201 <DeleteButton
202 className="btn btn-danger"
203 confirm
204 prompt="Nooooo! Please dont delete 😿"
205 >
206 <i className="fa fa-trash mr-2" />
207 Delete
208 </DeleteButton>
209 </DataObjectProvider>
210 );
211}
212
213function MyListComponent(props) {
214 const data = useData(dsMyDataObject);
215
216 return (
217 <DataObjectProvider value={dsMyDataObject}>
218 {data.map((record, idx) => (
219 <DeleteButton
220 className="btn btn-danger"
221 confirm
222 prompt={props =>
223 new Promise(resolve => {
224 if (checkIfRecordShouldBeDeleted(props.index)) {
225 resolve(true);
226 } else {
227 resolve(false);
228 }
229 })
230 }
231 index={idx}
232 >
233 <i className="fa fa-trash mr-2" />
234 Delete
235 </DeleteButton>
236 ))}
237 </DataObjectProvider>
238 );
239}
240```
241
242#### Timepicker
243
244Binds a time input to a date field. If the field doesn't have a value when the time is selected, the current date will be used.
245
246```jsx
247import { Timepicker } from "@olenbetong/data-binding";
248
249function MyComponent(props) {
250 return (
251 <DataObjectProvider value={dsMyDataObject}>
252 <Timepicker field="MyDateField" className="form-control" />
253 </DataObjectProvider>
254 );
255}
256```
257
258#### bind
259
260For custom functionality there is a small HoC available to create a bound component.
261
262If the current value should be assigned to a property other than "value", you can pass a `valueProp` option to the HoC. If you need to edit the value before it is put into the data object, you may bass a `valueConverter` function option. The argument passed to the onChange handler will be passed to the valueConverter.
263
264```jsx
265import { bind } from "@olenbetong/data-binding";
266
267function MyComponent(props) {
268 return (
269 <SomeComponent
270 something={props.customValueProperty}
271 onChange={props.onChange}
272 />
273 );
274}
275
276function addOneToValue(value) {
277 return value + 1;
278}
279
280export default bind(MyComponent, {
281 valueProp: "customValueProperty",
282 valueConverter: addOneToValue
283});
284```