Spreadsheets Versus Models - A Compromise
“Why can’t I copy a value and fill it into every line?”
When it comes to creating web apps, no matter how good your user interface is you will never be able to compete with the raw editing power and flexibility of Microsoft Excel. As the benefits of cloud based software become more apparent in industries that are more at home with spreadsheets, this shortcoming has become a frequent hurdle to user acceptance.
How can we provide a familiar experience to Excel, without compromising the integrity of our models?
Introducing React-Object-Table
React Object Table provides a framework for editable lists of objects based on a configuration of columns. Although it enforces some sanity and implements column validation, it does provide a great deal of the cell navigation and copy paste power users have come to expect from spreadsheets.
With a well defined table users can copy and paste data of any dimensions around the table, updating many rows and columns at a time. The clipboard is populated with both javascript data and a text-based representation, so it is even possible to copy data outside of the component into Excel, modify it, and paste it back into the table.
The validator functions will attempt to validate given data, and raise an error if the value is not appropriate for a given cell.
How does it work?
At its core, the table is just an array of JSON objects that is viewed through a configuration of columns. The columns define:
- The drawer used to render what the cell looks like when it is not being edited
- The editor used when a cell is being actively edited
Both of these settings define a React component for rendering, but additionally the editor comes with a validation function to clean and/or reject any input that isn’t appropriate.
Although the only out-of-the-box option is text, it is very easy to define your own drawers and editors. Some of the widgets we have built for our project include:
- Currency drawer
- Decimal editor
- Multiline text drawer and editor
- Date drawer and editor
- Checkbox drawer and editor
- Multi-select editor
- Percent drawer
The core table automatically handles:
- Arrow key cell navigation
- Cell selection
- Copy and paste behaviour
- Transitioning from viewing to editing a cell
Any change, whether it comes from a direct edit or from copy-paste, triggers the top level table onUpdate describing the changed object, to which you can decide how to proceed.
The table redraws are optimised, and thus expect the objects passed through in the props to be immutable.
What’s next for the project?
With our implementation of React Object Table, we wrap the table in an Api Table component to sync the frontend changes with our server. This works great for editing existing items that are created to a certain standard, but has some issues we would like to address:
- Users require a constant internet connection to make any changes to the data.
- There is no explicit “save” behaviour; all changes are made immediately in real time which is problematic when users would like to abort their changes.
- It is not possible for data to exist that would not pass our model validation; this results in a clunky interface for creating new records, and for any incremental changes that are deemed “invalid” by the server but would have been addressed by the user later.
In the coming months we will be improving our use of the package to support the above upgrades, which will most likely mean pulling the cell validation out of the cell’s scope to a higher level where it can be optionally addressed, rendered, or ignored.
Along with these changes, some future enhancements planned are:
- Column and row-level selection and copy-paste.
- Inline column filtering and ordering.
- Open sourcing some of our more commonly used drawers and editors as optional extras. These are currently not included to reduce dependencies.
What is intentionally not planned for the project?
A frequent suggestion that comes up when people are shown React-Object-Table is “why don’t you add virtualisation so you can have thousands upon thousands of rows?”. Although it would be technically feasible, I am opposed to this direction for the package for a number of reasons:
- Loading an enormous data set, even in increments, is a bad user experience that can be solved in much more elegant ways. No user ever actually intends to visually work with thousands of rows at a time, a filter interface is the most common more appropriate solution (a blog post on api based filtering will be coming soon).
- Virtualised interfaces feel clunky and are more prone to implementation problems
- Adding virtualisation would require abolishing or mangling the current correct HTML table structure
Along the same lines as virtualisation, pagination for large data sets has been suggested. Ultimately however, if the data set is too large then an editor like React-Object-Table is not the best solution. It makes it too easy to change huge swathes of data accidentally, and with pagination you lose the convenience of keyboard navigation / selecting areas.
If you’d like to contribute, please check out the github project: https://github.com/uptick/react-object-table
For an interactive sample of the current stable version, check out the demo page at:
https://uptick.github.io/react-object-table
Note: Copy paste functionality does not work on Firefox.
For instructions on how to add react-object-table to your project, head to the readme page:
https://github.com/uptick/react-object-table/blob/master/README.md