…for the Hypergrid Cell Formatter & Editor Workbench
This document discusses the workbench user
interface and concludes with a tutorial/example.
This workbench is for learning about Hypergrid cell formatting and editing using localizers and cell editors.
You can also use it for actual development and testing of these objects, but be aware this app does not persist your work anywhere.
Be careful therefore to cut & paste anything you want to keep!
Data pane
A data pane is provided to adjust the data to suit the needs of your localizers and cell editors.
Bind the data to the grid by clicking grid.setData(…) button.
The grid in turn is bound to the data window for any edits made to cells using cell editors.
State pane
This pane lets you add a set of properties declaratively to the grid.
Properties can also be set programmatically from the console with the various set property methods.
Edit the state pane to make the prevclose column use the ft-in (“foot-inch”) localizer rather than the trend localizer.
Click the grid.addState(…) button.
Notice how these values are now formatted as feet and inches.
Double click one of these formatted values in the grid. Cell editing is enabled because:
The cell’s editable property is truthy.
The cell’s editor property is defined and resolves to a registered cell editor.
The cell’s ft-in localizer has a parse method. (You can see the localizer by selecting it from the dropdown in the Localizer pane.)
Notice how editing in the grid is done in the format specified by the localizer — unlike the in the Data pane where these numbers are integers. The cell editor uses the localizer’s parse function to turn the edited value back into an integer.
Localizer pane
Value formatting in Hypergrid is controlled by localizers, objects with the following shape:
{
format: function(str), // required method that returns value
parse: function(value), // required method that returns string
invalid: function(value), // optional helper function for `parse`
expectation: "optional error message describing expected syntax"
}
Formatting grid cell values is purely a text string manipulation. Any other graphical effects are the purview of cell renderers (not covered here). Cell editors can include fancy controls for manipulating the string, but the result needs to be a formatted string, parsable into a serializable value for storing into the data model.
Required.
Parses values in the displayed format, converting them back into raw data values. Used by cell editors to store results back to data model, or by search algorithms to compare a formatted value to other cells' raw values (without having to format every on of the other cells), etc.
Although parse is only called by Hypergrid when a formatted cell is edited, an implementation is currently required (by add). The reason for this is "completeness" — to be ready and available when your application inevitably needs to parse a value later on. The assumption is it’s easier to write a parser when you write the formatter than to have to go back later and rethink it. (That said, you can provide a no-op implementation to fake out add but you do so at your peril. So long as the cells using this localizer are not editable you should be all right.)
Should be implemented when parse is not bulletproof (when it would jam unexpectedly on bad syntax). When implemented, invalid is called first; parse is only called if the syntax is valid (invalid returns falsy; i.e., “not invalid”).
(Notice incidentally how add can optionally accept a name parameter for localizers that lack a name property.)
Bind the localizer to a cell
Bind a localizer using the format property. As always, a property can be set for the entire grid, a specific column (or row), or a specific cell.
There are many ways to do this programmatically from the console with the various property setting methods.
From this interface, edit the state and click the grid.addState(…) button:
State object to set grid format property to 'deg':
Procedure is similar to that for localizer (see above).
Duplicate an existing cell editor
Procedure is similar to that for localizer (see above).
Grid
The live Hypergrid appears below the other panes.
Tutorial / Example
The prevclose column is set to use this localizer (see the State pane), whose format function formats positive numbers with an up-arrow (⬆) and negative numbers with a down-arrow (⬇). The data for this column are all initially positive (see Data pane), so all values are formatted with up-arrows (see Grid).
Edit the cell value in the data pane
In the data pane, change a couple of the prevclose values to negative numbers.
Click the grid.setData(…) button.
Notice how these values are now formatted with down-arrows in the grid.
Edit the cell in the grid
Double click one of these formatted values in the grid to edit it using the Textfield cell editor.
Cell editors append additional elements to the DOM, positioned precisely to the cell bounds.
Edit the cell. This editor is for straight-forward text editing; it has no special controls for handling the arrows which are arrows are not available on the keyboard. You could cut & paste a down-arrow from another cell; or as a special accommodation to this problem the trend localizer’s parse function also accepts a prefixed negative sign.
The trend localizer’s parse function has a problem. It doesn't properly throw an error on failure:
Double-click another cell to edit it, seting the contents to some invalid value (such as “xyz” which is not a number).
Press the enter key.
The parser cannot convert the value.
Editor is closed.
Grid is updated with null.
Data pane is also updated with null.
The solution is to have the parse function throw an error when it cannot convert the value. If the native window.parseFloat function threw an error on failure we'd be there already. Instead, it returns NaN (“not anumber”) on failure. Let’s catch the NaN and throw an actual error:
In the Localizer pane, select the trend localizer.
Uncomment the if statement with the throw statement within it.
Click the grid.localization.add(…) button to update it.
Edit another cell, again setting the contents to some invalid value.
Press the enter key.
The parser cannot convert the value.
Negative feedback animation plays.
Every third failure (press enter again) is accompanied by an alert with additional information (the error’s message is shown in the alert).
Check for valid syntax
The parse function as implemented correctly parses expected input. However, it’s not a fully realized finite-state machine; it doesn't parse the input as a grammar and therefore doesn't catch invalid syntax. Specifically, this parser ignores extraneous characters between a valid floating point number and the end of string, which conveniently ignores the arrows, but also anything else. There are two choices for addressing this:
Make the parser "bulletproof" by implementing an FSM (from scratch or via a RegExp).
Implement an invalid function to do a syntax pre-scan, which allows a (simpler) parser to assume the syntax is valid.
Display syntax hint
If you implement the expectation string property, that string (a friendly description of the syntax) will be included in the error alert mentioned above. If parse is called and it throws an error with a defined message property, that message will also be included (prepended to the expectation message).
Cell editors
We haven't discussed cell editors so far. Often, the Textfield cell renderer + a localizer with well-defined format and parse methods is all you need. A custom cell renderer is needed when what you’re editing cannot be easily represented or conveniently edited as text, or if you want to code special event handlers (such as ignoring illegal chars).
Bind to a cell editor
Cell editors depend on localizers and always have a default localizer that is used when the cell does not define a custom localizer (in its format property). Custom cell editors generally work hand-in-hand with a custom localizer. Let’s add a simple editor to the prevclose column. The column already specifies a custom localizer, trend. We're going to bind a custom cell editor to this column, also called Trend.
Note: The get methods, such as grid.localization.get(localizerName) and grid.cellEditors.get(cellEditorName) treat their parameter as case-insensitive. Localizer and cell editor names must be unique but exist in different name spaces which is why these two names don't clash (not because of the case differences.) The cell editor here could have been called "trend" but cell editors by convention start with a capital letter because they’re object constructors (or "classes" — which by long-standing convention are always capitalized).
In the State pane, add editor: "Trend" to the prevclose column.
Click the grid.addState(…) button.
Double-click a cell in the prevclose column.
Click the arrow and notice how it flips.
Press the enter key to save the change or esc key to cancel it.
This cell editor makes toggling the arrow a simple matter of clicking on it. This usually would not be worth writing a cell editor for — except that in this case, the Unicode arrows are not on the keyboard so they're impossible to type.
Edit the cell editor
Let's add a keypress handler to provide a way to type the up-arrow and down-arrow.
Take a look at the Trend cell editor:
In the Cell Editor pane, select "Trend" from the drop-down.
Uncomment the keypress event handler.
Click the grid.cellEditors.add(…) button.
Double-click a cell in the prevclose column.
Type + or − and notice how the arrow flips.
This keypress handler does two things:
The case '+' and case '-' cases let the user "type" in the arrows at any time during editing by pressing the + or − keys for up-arrow or down-arrow, respectively.
The default case discards unexpected key presses, triggering an error feedback animation.
Because the workbench's cell editors are dynamically added and don't exist as code files, debugging is challenging. Here’s one way to see your cell editor's code page:
Make sure you have an initialize method (even if it’s empty).
From the browser console, load up src/cellEditors/CellEditor.js.
Place a breakpoint on the last line of the initialize method. If empty, you can place the breakpoint on the closing brace (}).
Open the cell for editing by double-clicking it.
From the breakpoint do 1 × "step-over" + 3 × "step-into" and you should find yourself in the subclass’s initialize method. (To get to a sub-subclass: 1 × "step-out" + 3 × "step-into")
Now that the debugger is displaying your cell editor, you can set breakpoints in your other methods.