@olenbetong/data-binding v0.16.2
data-binding
Components 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.
Getting started
Installation
The library is published to NPM. To use, install from the command line
npm i @olenbetong/data-binding
Or use the IIFE build from unpkg.com
<script
crossorigin
src="https://unpkg.com/@olenbetong/data-binding@latest/dist/iife/data-binding.min.js"
type="text/javascript"
></script>
Context
Use the DataObjectProvider to provide data object context to components in the child tree.
This is the equivalent of setting data-object-id on an element in Appframe data binding.
import {
DataObjectProvider,
BoundInput
} from "/file/component/modules/esm/data-binding.min.js";
function MyComponent(props) {
return (
<DataObjectProvider dataObject={dsMyDataObject}>
<BoundInput type="text" field="MyField" />
</DataObjectProvider>
);
}
Components
BoundInput
Renders an input with a 2-way binding to a field in the data object passed by data object context.
import {
DataObjectProvider,
BoundInput
} from "/file/component/modules/esm/data-binding.min.js";
function MyComponent(props) {
return (
<DataObjectProvider value={dsMyDataObject}>
<BoundInput type="text" field="MyField" />
</DataObjectProvider>
);
}
BoundSelect
Loads 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.
There are two ways to set the options from the data object:
- Pass a function to the
option
property. This should take the record as the first argument and return a React node () - Set the
valueField
to the data object field that represents the value. Optionally set thedescriptionField
to another field that should be shown in the dropdown.
If 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.
Other properties:
dataObject
- Data object used to list the optionsfilter
- Filter passed to the data objectfield
- Field to bind the value tonullable
- If true, will add an empty option (default:true
)
The components in this example will create the same select using the 2 different methods above:
import { DataObjectProvider, BoundSelect } from "@olenbetong/data-binding";
function MyComponentWithOptionFunc() {
return (
<BoundSelect
dataObject={dsSomeDataObject}
filter="IsAGoodRecord = 1"
option={record => <option value={record.Value}>{record.Name}</option>}
/>
);
}
function MyComponentWithFields() {
return (
<BoundSelect
dataObject={dsSomeDataObject}
filter="IsAGoodRecord = 1"
valueField="Value"
descriptionField="Name"
/>
);
}
function MyComponentWithUnboundOptions() {
return (
<BoundSelect field="SomeEnumerableField">
<option value="value1">Value 1</option>
<option value="value2">Value 2</option>
<option value="value3">Value 3</option>
</BoundSelect>
);
}
function MyComponentWithCombinedOptions() {
return (
<BoundSelect
field="SomeEnumerableField"
dataObject={dsSomeDataObject}
filter="IsAGoodRecord = 1"
valueField="Value"
descriptionField="Name"
>
<option value="value1">Value 1</option>
<option value="value2">Value 2</option>
<option value="value3">Value 3</option>
</BoundSelect>
);
}
SaveButton
Calls endEdit on the data object passed by data object context.
Unlike 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.
import { DataObjectProvider, SaveButton } from '/file/component/modules/esm/data-binding.min.js';
function MyOuterComponent(props) {
return (
<DataObjectProvider value={dsMyDataObject}>
<MySaveButton />
</DataObjectProvider>
)
}
function MyComponent(props) {
const dataObject = useContext(DataObjectProvider);
const isDirty = useDirty(dataObject);
return (
<SaveButton disabled={!isDirty} className='btn btn-secondary'>
<i className='fa fa-save mr-2'/>
Save changes
</SaveButton>
)
CancelButton
Calls cancelEdit on the data object passed by data object context.
import { DataObjectProvider, CancelButton } from '/file/component/modules/esm/data-binding.min.js';
function MyComponent(props) {
return (
<DataObjectProvider value={dsMyDataObject}>
<CancelButton className='btn btn-secondary'>
Cancel changes
</CancelButton>
</DataObjectProvider>
)
DeleteButton
Deletes 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.
The 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
.
The delete button also accepts an index
property to be able to use it outside of the current row. E.g. when listing the records.
import {
DataObjectProvider,
DeleteButton
} from "/file/component/modules/esm/data-binding.min.js";
function MyComponent(props) {
return (
<DataObjectProvider value={dsMyDataObject}>
<DeleteButton
className="btn btn-danger"
confirm
prompt="Nooooo! Please dont delete 😿"
>
<i className="fa fa-trash mr-2" />
Delete
</DeleteButton>
</DataObjectProvider>
);
}
function MyListComponent(props) {
const data = useData(dsMyDataObject);
return (
<DataObjectProvider value={dsMyDataObject}>
{data.map((record, idx) => (
<DeleteButton
className="btn btn-danger"
confirm
prompt={props =>
new Promise(resolve => {
if (checkIfRecordShouldBeDeleted(props.index)) {
resolve(true);
} else {
resolve(false);
}
})
}
index={idx}
>
<i className="fa fa-trash mr-2" />
Delete
</DeleteButton>
))}
</DataObjectProvider>
);
}
Timepicker
Binds 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.
import { Timepicker } from "@olenbetong/data-binding";
function MyComponent(props) {
return (
<DataObjectProvider value={dsMyDataObject}>
<Timepicker field="MyDateField" className="form-control" />
</DataObjectProvider>
);
}
bind
For custom functionality there is a small HoC available to create a bound component.
If 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.
import { bind } from "@olenbetong/data-binding";
function MyComponent(props) {
return (
<SomeComponent
something={props.customValueProperty}
onChange={props.onChange}
/>
);
}
function addOneToValue(value) {
return value + 1;
}
export default bind(MyComponent, {
valueProp: "customValueProperty",
valueConverter: addOneToValue
});
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago