Minimizing Dijit Instances in dgrid

By on April 14, 2015 11:22 am

dgrid

Dijit and dgrid provide a powerful set of user interface components, allowing for fast construction of sophisticated web applications with excellent performance and interactivity. However, one particular configuration of dgrid that can impact memory and performance: heavy use of persistent Dijit editors within grid cells. The dgrid’s Editor plugin makes it very easy to leverage various Dijits for editing cells. However, using the default “always-on” mode, where a Dijit is instantiated for every editable cell, in combination with a several columns with numerous rows can quickly consume a lot of memory and create a noticeable drag on your application’s performance.

Dijit editors can help provide some fantastic functionality, with powerful widgets for auto-completion, date input, and validation. In this post, we’ll review two approaches to achieving advanced data input in dgrids that are designed for heavy data manipulation without compromising performance or memory consumption.

editOn

The simplest and most straightforward way to limit the creation of Dijit widgets is by creating one shared instance of an editor per dgrid column using the editor’s editOn property. This editor plugin is designed to be used such that editors, including widgets, are not displayed until the user initiates interaction with the cell, and the editOn allows us to enable this behavior, and specify what type of event should trigger the editor. Typically, we might enable the editor with a click event, or possibly a dblclick event. For example:

var grid = new Grid({
	columns: {
		dueDate: {
			editor: DateTextBox,
			editOn: 'click',
			label: 'Due'
		},
...

When editor columns are configured with the editOn property, the cells will be rendered using the standard default high-performance rendering path that simply generates text inside of a cell. Only a single shared Dijit will then be created, and it will be displayed in the appropriate cell when the editOn event is triggered. In this case, the single Dijit editor will only be displayed in response to a click event.

One of the issues with this approach, is that typically the rendering of the grid cells might look quite different than the rendering of the Dijit controls. In some situations this may be desirable, clearly differentiating and highlighting the cell that is currently being edited. However, in some situations this might not be desirable, as it might not be clear to users to that the cells are editable. Visually indicating that cells are editable may be particularly important in situations where cells might be editable or non-editable in different situations. Different rows might be editable, different columns might be editable, or the cells might switch between editable and read-only over time (e.g. switching application modes). Rendered Dijit editors provide a distinct styling that visually invites editing.

However, we can mitigate this issue by styling the editable cells with presentation similar to Dijit editors. For standard text boxes, this could be as simple as creating an inner element with a border and appropriate spacing, that mimics a Dijit TextBox. We could create such an inner element by adding a renderCell method to create one:

columnDefinition: {
	editor: TextBox,
	editOn: 'click',
	renderCell: function (object, value, td) {
		put(td, 'div.pseudoEditor', value);
	},
...

And then we could add some styling. This will look similar to a Claro-themed TextBox (although again, you might actually want some differences to more distinctly highlight the cell that is being edited):

.pseudoEditor {
	border: 1px solid #b5bcc7;
	height: 13px;
	padding: 2px;
}

Other widgets may require more attention to provide similar styling. For example, if you wanted to mimic the dropdown button/arrow of a DateTextBox, Select, or ComboBox, you would need to provide a corresponding element (or psuedo-element) and icon. You might also want to listen for click events on this pseudo dropdown element, and in response to such a event, instantiate the widget and then open the dropdown of the widget after it has been rendered (usually done by calling the widget’s loadAndOpenDropDown method).

Customized Native Inputs with Event Delegation

There are situations where the editOn approach might not be ideal. If you are using validation, you may wish for the validation indicator to persist even after a user has moved on to other cells. With the editOn configuration, the widget will be removed once the user is done editing (on the blur event), and so validation won’t be naturally retained. Also, there may be situations where you wish to provide specialized input handling, that Dijit doesn’t provide. Or maybe you don’t want to load the Dijit dependencies (perhaps for lightweight mobile applications). While this is typically a more involved approach, it is very doable.

To begin, rather than configuring our editor to use a widget, we will define an input to use as the editor. We can do this by providing a string to the editor property value. This string will be used as the type attribute value for a native <input>. This will give us some basic editing capability. From here, we could then setup an event listener at the grid level, using the concept of event delegation, to listen for any events of interest from the editors. This can give us a chance to respond to click events to open any drop downs, or dgrid’s dgrid-datachange events to perform validation and add appropriate validation indicators to the cell. Here is an example of listening to dgrid-datachange events to perform validation:

grid.on('.field-someProperty:dgrid-datachange', function (event) {
	var cell = grid.cell(event);
	var value = event.value;
	var valid = isValid(value);
	// add or remove the error-state class based on if the value is valid
	put(cell.element, valid ? '!error-state' : '.error-state');
	if (!valid) {
		// prevent the value from changing
		event.preventDefault();
	}
});

We might want to build on this to add a mouse hover handler to show a tooltip with more validation error information.

The editor can easily be configured for a native input element based on an <input>. However, if you want to use another input element, such as a <select> element, this will require additional effort. This could be accomplished by writing your renderCell method to generate the <select> element and writing your own change event handler, and manually updating data values in response to changes in the input.

Another approach is to mimic a widget with the editor value that you have provided to the dgrid editor (make the editor think it is a widget). This is somewhat of a fragile approach, since it relies on undocumented interactions in the editor, but you can provide a function as an editor (if the editor is a function, dgrid treats it as a widget, and your function will be executed for each cell, as if it were a widget constructor), and assign a self-reference of the focusNode property, and define a connect to handle event handling, to handle the necessary calls and property interactions that dgrid will make to the widget:

someColumn: {
	editor: function (args) {
		var select = ... create the select or other input element
		// the editor keys of the focusNode to get the element
		select.focusNode = select;
		// provide the event handling function that the editor will use
		// to register for listening to change events
		select.connect = eventHandlerForTheseElements;
	}

Using custom renderers and custom event delegation handling, virtually any type of input can be created efficiently.

Again, we recommend optimizing the number of Dijit inputs within a grid to maximize performance. Creating Dijit inputs through a dgrid can have an impact on performance. For editors that can be appropriately styled while they aren’t being edited, the editOn approach can make it possible to only instantiate a single shared Dijit editor instance per dgrid column. If you require editors with persistent customized behavior, like validation, using native inputs with event delegation to customize the behavior may be your best option. If you are experiencing performance problems with grids in your application, we would love to help. A properly optimized dgrid is extremely fast and fluid, and you (and your users) shouldn’t settle for less.

Comments