SlickGrid简单介绍 : https://github.com/mleibman/SlickGrid/wiki
快速入门 : https://github.com/mleibman/SlickGrid/wiki/Getting-Started
使用示例 : https://github.com/mleibman/SlickGrid/wiki/Examples
API文档: https://github.com/mleibman/SlickGrid/wiki/API-Reference
处理选择模式: https://github.com/mleibman/SlickGrid/wiki/Handling-selection
Welcome to the SlickGrid!
UPDATE: March 5th, 2014 – I have too many things going on in my life right now to really give SlickGrid support and development the time and attention it deserves. I am not stopping it, but I will most likely be unresponsive for some time. Sorry.
UPDATE: This repo hasn’t been updated in a while. https://github.com/6pac/SlickGrid/wikiseems to be the most active fork at the moment.
Do you use SlickGrid? Add your site to the Used By!
Want to show your appreciation? Hit Donate! :)
What it is
Quite simply, SlickGrid is a JavaScript grid/spreadsheet component.
It is an advanced component and is going to be a bit more difficult to learn
and configure, but once you realize its full potential, it will blow your mind!
Some highlights:
- Adaptive virtual
scrolling (handle hundreds of thousands of rows with extreme responsiveness) - Extremely fast rendering
speed - Supports jQuery UI Themes
- Background post-rendering
for richer cells - Configurable &
customizable - Column
resize/reorder/show/hide - Column autosizing &
force-fit - Pluggable cell formatters
& editors - Support for editing and
creating new rows. - Grouping, filtering,
custom aggregators, and more! - Advanced detached &
multi-field editors with undo/redo support. - “GlobalEditorLock” to
manage concurrent edits in cases where multiple Views on a page can edit
the same data.
Resources
Dependencies
- jQuery
- jQueryUI Sortable
(optional, only if column reordering is enabled) - jquery.event.drag –
http://threedubmedia.com/code/event/drag - jquery.event.drop –
http://threedubmedia.com/code/event/drop
Documentation
-
Getting Started – A brief
introduction to some of SlickGrid’s basic concepts. -
Examples – A broad set of
examples that demonstrate the most effective ways of using SlickGrid
(probably the best place to learn how to use it). - API Reference
- Handling selection
- All pages on this wiki
Contact/Support
GitHub Issues
are for bug reports only. Please use the appropriate forum.
Please see Rules of conduct
- Asking questions
- SlickGrid questions on
* -
SlickGrid Google Group – For support
requests and conversation about SlickGrid. - @slickgrid on Twitter
- Reporting bugs
When reporting
bugs, please be specific (details are described in the above “Rules of
conduct”):
1. Include the
version of SlickGrid.
2. Include the reproduction steps. Describe about expected behavior &
actual behavior.
(If possible, post code on jsfiddle.net).
3. Use proper English that everybody can understand.
What makes it different
Virtual rendering
SlickGrid
utilizes virtual rendering to enable you to easily work with hundreds of
thousands of items without any drop in performance. In fact, there is no
difference in performance between working with a grid with 10 rows versus a
100’000 rows. This is achieved through virtual rendering where only what’s
visible on the screen plus a small buffer is rendered. As the user
scrolls, DOM nodes are continuously being created and removed. These
operations are highly tuned to provide optimal performance under all browsers.
The grid also adapts to the direction and speed of scroll to minimize the
number of rows that need to be swapped out and to dynamically switch between
synchronous and asynchronous rendering.
It does a few
other things to maximize performance, such as dynamically generating and updatingCSS rules,
so that resizing a column does not change the grid DOM tree and only
causes one reflow, and loading cell editors asynchronously to maximize keyboard
navigation speed.
Grid vs Data
The key
difference is between SlickGrid and other grid implementation I have seen is
that they focus too much on being able to understand and work with data
(search, sort, parse, ajax load, etc.) and not enough on being a better “grid”
(or, in case of editable grids, a spreadsheet). It’s great if all you want to do
is “spruce up” an HTML TABLE or slap a front end onto a simple
list, but too inflexible for anything else.
Data is
complicated. It has business rules. It has non-intrinsic properties. Editing
one property of an element can lead to cascading changes modifying other
properties or even other elements. It has dependencies. What I’m saying, is
that dealing with data is best left to the developer using the grid control.
Trying to fit all of that into the grid implementation and API will
only limit its applicability and add considerable bloat.
SlickGrid takes
a different approach. In the simplest scenario, it accesses data through an
array interface (i.e. using “dataitem” to get to an item at a given position
and “data.length” to determine the number of items), but the API is
structured in such a way that it is very easy to make the grid react to any
possible changes to the underlying data.
Responding to data source changes
While this may
not be immediately obvious from what you see in the examples and in the code,
the key use of the grid is in MVC applications where the grid is
wired to respond to events in the Model. In our application we have spreadsheet
component of a Gantt chart, and the Model is a “filtered” view of the tasks
(rows) in the original datasource. Suppose you collapse or expand a parent task
or enter some text in a Quick Filter textbox. The Model then recalculates which
rows are now visible, compares that with what was visible before and fires two
events – onRowCountChanged & onRowsChanged. The latter one tells all
subscribed Views, such as the grid, that the rows in specific positions
changed. The grid then only has to invalidate/remove those rows and call grid.renderViewport()
to make sure that whatever is in the viewport is visible. The onRowCountChanged
event triggers the recalculation of the virtual canvas – grid.resizeCanvas().
Together, this pattern makes for an incredibly efficient, flexible and, most importantly,
scalable implementation.
Unsupported browsers
Rather than
listing all the browsers that SlickGrid works on, I will concentrate on the
ones that don’t.
IE6 is
explicitly NOT supported. While some people have made SlickGrid work
with IE6, I am not going to merge those changes in or spend any time testing on
IE6 and trying to make it work. If you have to support it, I feel your pain,
but I don’t want to share it.
Opera seems to
be having some issues with synchronized scrolling of column headers and a
vertical page scrollbar appearing when scrolling the grid content vertically. I
haven’t been able to figure out why in the amount of time I was willing to
spend on it. If you have an idea, let me know.
Thanks
Big thanks to
JetBrains who have supported SlickGrid and other open-source projects by
providing a free license for their awesome WebStorm IDE.
This is the API
reference for SlickGrid.
- Constructor - Slick.Grid constructor
- Core - Core grid functionality
- Columns - Initializing and customizing columns
- Cells - Manipulating cell data and styling
- Rendering - Rendering methods
- Headers - Column header methods
Table of Contents
- Constructor
- Core
- Columns
- autosizeColumns
- getColumnIndex
- getColumns
- setColumns
- setSortColumn
- setSortColumns
- updateColumnHeader
- Cells
- addCellCssStyles
- canCellBeActive
- canCellBeSelected
- editActiveCell
- getActiveCell
- getCellEditor
- getCellFromEvent
- getCellFromPoint
- getCellNode
- getCellNodeBox
- gotoCell
- navigateDown
- navigateLeft
- navigateNext
- navigatePrev
- navigateRight
- navigateUp
- removeCellCssStyles
- resetActiveCell
- setActiveCell
- setCellCssStyles
- Rendering
- getCanvasNode
- getRenderedRange
- getViewport
- invalidate
- invalidateRow
- invalidateRows
- resizeCanvas
- scrollCellIntoView
- scrollRowIntoView
- scrollRowToTop
- updateCell
- updateRow
- updateRowCount
- Headers
# Constructor
# var grid = new Slick.Grid(container,
data, columns, options);
container -
Container node to create the grid in. This can be a DOM Element, a jQuery node,
or a jQuery selector.
data -
Databinding source. This can either be a regular JavaScript array or a custom
object exposing getItem(index) and getLength() functions.
columns - An array
of column definition objects. See Column
Options for a list of options that can be included on each column definition
object.
options -
Additional options. See Grid
Options for a list of options that can be included.
Create an
instance of the grid.
Example usage,
taken from the
basic Slickgrid example:
var grid;
var columns = [
{id: "title", name: "Title", field: "title"},
{id: "duration", name: "Duration", field: "duration"},
{id: "%", name: "%
Complete", field: "percentComplete"},
{id: "start", name: "Start", field: "start"},
{id: "finish", name: "Finish", field: "finish"},
{id: "effort-driven", name: "Effort Driven", field: "effortDriven"}
];
var options = {
enableCellNavigation: true,
enableColumnReorder: false
};
$(function () {
var data = [];
for (var i = 0; i < 500; i++) {
data[i] = {
title: "Task
" + i,
duration: "5
days",
percentComplete: Math.round(Math.random() * 100),
start: "01/01/2009",
finish: "01/05/2009",
effortDriven: (i % 5 == 0)
};
}
grid = new Slick.Grid("#myGrid", data, columns, options);
# Core
# grid.init()
Initializes the grid. Called after
plugins are registered. Normally, this is called by the constructor, so you
don't need to call it. However, in certain cases you may need to delay the
initialization until some other process has finished. In that case, set
the explicitInitialization option to true and call
the grid.init() manually.
# grid.getData()
Returns an array of every data object,
unless you're using DataView in which case it returns a
DataView object.
# grid.getDataItem(index)
index - Item
index.
Returns the
databinding item at a given position.
// Get the id of the 15th item
var id15 = grid.getDataItem(14).id;
# grid.setData(newData, scrollToTop)
data - New
databinding source. This can either be a regular JavaScript array or a custom
object exposing getItem(index) and getLength() functions.
scrollToTop - If true,
the grid will reset the vertical scroll position to the top of the grid.
Sets a new source for databinding and
removes all rendered rows. Note that this doesn't render the new rows - you can
follow it with a call to render() to do that.
# grid.getDataLength()
Returns the size
of the databinding source.
// Create an array of just the ids from every data item
var ids = [];
for (var i=0; i<grid.getDataLength() ; i++) {
ids.push(grid.getDataItem(i).id);
}
# grid.getOptions()
Returns an
object containing all of the Grid options set on the grid. See
a list of Grid Options here.
// Find all elements that are currently selected
var $selectedCells = $('.' + grid.getOptions().selectedCellCssClass);
# grid.getSelectedRows()
Returns an array
of row indices corresponding to the currently selected rows.
# grid.getSelectionModel()
Returns the
current SelectionModel. See
here for more information about SelectionModels.
# grid.setOptions(options)
options - An
object with configuration options.
Extends grid
options with a given hash. If there is an active edit, the grid will attempt to
commit the changes and only continue if the attempt succeeds.
// set a new CSS class for selected cells
grid.setOptions( {
selectedCellCssClass: "newSelection" } );
// Select the first row
grid.setSelectedRows([0]);
// get the elements for the selected cells
$('.newSelection');
# grid.setSelectedRows(rowsArray)
rowsArray - An array
of row numbers.
Accepts an array of row indices and
applies the current selectedCellCssClass to the
cells in the row, respecting whether cells have been flagged as selectable.
// Select the first three rows
grid.setSelectedRows([0, 1, 2]);
# grid.setSelectionModel(selectionModel)
selectionModel - A SelectionModel.
Unregisters a
current selection model and registers a new one. See the
definition of SelectionModelfor more information.
# Columns
# grid.autosizeColumns()
Proportionately
resizes all columns to fill available horizontal space. This does not take the
cell contents into consideration.
# grid.getColumnIndex(id)
id - A column
id.
Returns the
index of a column with a given id. Since columns can be reordered by the user,
this can be used to get the column definition independent of the order:
var column = grid.getColumns()[grid.getColumnIndex("title")]
# grid.getColumns()
Returns an array
of column definitions, containing the option settings for each individual
column.
// Log to console whether the first column is sortable
var cols = grid.getColumns();
var sortable = cols[0].sortable;
sortable ? console.log("It's
sortable!") : console.log("It's not
sortable!");
# grid.setColumns(columnDefinitions)
columnDefinitions - An array
of column definitions.
Sets grid columns. Column headers will
be recreated and all rendered rows will be removed. To re-render the grid (if
necessary), call render().
// Change the name of the first column to
"First"
var data = grid.getColumns();
data[0].name = "First";
grid.setColumns(data);
# grid.setSortColumn(columnId,
ascending)
Accepts a columnId string and
an ascending boolean. Applies a sort glyph in either ascending
or descending form to the header of the column. Note that this does not actually
sort the column. It only adds the sort glyph to the header.
# grid.setSortColumns(cols)
Accepts an array of objects in the
form [ { columnId: [string], sortAsc: [boolean] }, ... ]. When called,
this will apply a sort glyph in either ascending or descending form to the
header of each column specified in the array. Note that this does not actually
sort the column. It only adds the sort glyph to the header
# grid.updateColumnHeader(columnId,
title, toolTip)
id - Column
id.
title - New
column name.
toolTip - New column
tooltip.
Updates an
existing column definition and a corresponding header DOM element with the new
title and tooltip.
// Change the column with an id of 'FirstName' to have
the name "A First Name", and no tooltip.
grid.updateColumnHeader("FirstName", "A First Name");
# Cells
# grid.addCellCssStyles(key,
hash)
key - A unique
key you can use in calls to setCellCssStyles and removeCellCssStyles. If a hash
with that key has already been set, an exception will be thrown.
hash - A hash
of additional cell CSS classes keyed by row number and then by column id.
Multiple CSS classes can be specified and separated by space.
Example:
{
0:
{
"number_column":
"cell-bold",
"title_column":
"cell-title cell-highlighted"
},
4:
{
"percent_column":
"cell-highlighted"
}
}
Adds an
"overlay" of CSS classes to cell DOM elements. SlickGrid can have
many such overlays associated with different keys and they are frequently used
by plugins. For example, SlickGrid uses this method internally to decorate
selected cells with selectedCellCssClass (see options).
# grid.canCellBeActive(row,
col)
row - A row
index.
col - A column
index.
Returns true if you can
click on a given cell and make it the active focus.
# grid.canCellBeSelected(row,
col)
row - A row
index.
col - A column
index.
Returns true if
selecting the row causes this particular cell to have the selectedCellCssClassapplied to it. A
cell can be selected if it exists and if it isn't on an empty / "Add
New" row and if it is not marked as "unselectable" in the column
definition.
# grid.editActiveCell(editor)
editor - A
SlickGrid editor (see examples in slick.editors.js).
Attempts to switch the active cell into
edit mode. Will throw an error if the cell is set to be not editable. Uses the
specified editor, otherwise defaults to any default editor for that given
cell.
// Assuming slick.editors.js is included...
// Set the first cell in the first row to be active
grid.setActiveCell(0,0);
// Invoke the Date editor on that cell
grid.editActiveCell(Slick.Editors.Date);
# grid.flashCell(row, cell, speed)
row - A row
index.
cell - A column
index.
speed (optional)
- The milliseconds delay between the toggling calls. Defaults to 100 ms.
Flashes the cell
twice by toggling the CSS class 4 times.
# grid.getActiveCell()
Returns an
object representing the coordinates of the currently active cell:
{
row: activeRow,
cell: activeCell
}
# grid.getActiveCellNode()
Returns the DOM
element containing the currently active cell. If no cell is active, null is
returned.
// Get the element for the active cell
var $active = $(grid.getActiveCellNode())
// Add a new class to the active cell
$active.addClass('myClass');
# grid.getActiveCellPosition()
Returns an
object representing information about the active cell's position. All
coordinates are absolute and take into consideration the visibility and
scrolling position of all ancestors. The object takes the form:
{
bottom:
[numPixels],
height:
[numPixels],
left:
[numPixels],
right:
[numPixels],
top:
[numPixels],
visible: [boolean],
width: [numPixels]
}
# grid.getCellCssStyles(key)
key - A
string.
Accepts a key name, returns the group of
CSS styles defined under that name. SeesetCellCssStyles for more info.
# grid.getCellEditor()
Returns the active
cell editor. If there is no actively edited cell, null is returned.
# grid.getCellFromEvent(e)
e - A
standard W3C/jQuery event.
Returns a hash
containing row and cell indexes from a standard W3C/jQuery event.
# grid.getCellFromPoint(x,
y)
x - An x
coordinate.
y - A y
coordinate.
Returns a hash
containing row and cell indexes. Coordinates are relative to the top left
corner of the grid beginning with the first row (not including the column
headers).
# grid.getCellNode(row, cell)
row - A row
index.
cell - A column
index.
Returns a DOM
element containing a cell at a given row and cell.
# grid.getCellNodeBox(row,
cell)
row - A row index.
cell - A column
index.
Returns an
object representing information about a cell's position. All coordinates are
absolute and take into consideration the visibility and scrolling position of
all ancestors. The object takes the form:
{
bottom:
[numPixels],
height:
[numPixels],
left:
[numPixels],
right:
[numPixels],
top:
[numPixels],
visible: [boolean],
width:
[numPixels]
}
# grid.gotoCell(row, cell, forceEdit)
Accepts a row integer
and a cell integer, scrolling the view to the row where row is its row
index, and cell is its cell index. Optionally accepts a forceEdit boolean
which, if true, will attempt to initiate the edit dialogue for the field in the
specified cell.
Unlike setActiveCell, this scrolls
the row into the viewport and sets the keyboard focus.
# grid.navigateDown()
Switches the
active cell one row down skipping unselectable cells. Returns a boolean saying
whether it was able to complete or not.
# grid.navigateLeft()
Switches the active cell one cell left
skipping unselectable cells. Unline navigatePrev,navigateLeft stops at
the first cell of the row. Returns a boolean saying whether it was able to
complete or not.
# grid.navigateNext()
Tabs over active
cell to the next selectable cell. Returns a boolean saying whether it was able
to complete or not.
# grid.navigatePrev()
Tabs over active
cell to the previous selectable cell. Returns a boolean saying whether it was
able to complete or not.
# grid.navigateRight()
Switches the active cell one cell right
skipping unselectable cells. Unline navigateNext,navigateRight stops at
the last cell of the row. Returns a boolean saying whether it was able to
complete or not.
# grid.navigateUp()
Switches the
active cell one row up skipping unselectable cells. Returns a boolean saying
whether it was able to complete or not.
# grid.removeCellCssStyles(key)
key - A string
key.
Removes an "overlay" of CSS
classes from cell DOM elements. See setCellCssStyles for
more.
# grid.resetActiveCell()
Resets active
cell.
# grid.setActiveCell(row,
cell)
row - A row
index.
cell - A column
index.
Sets an active
cell.
# grid.setCellCssStyles(key,
hash)
key - A string
key. Will overwrite any data already associated with this key.
hash - A hash
of additional cell CSS classes keyed by row number and then by column id.
Multiple CSS classes can be specified and separated by space.
Example:
{
0:
{
"number_column":
"cell-bold",
"title_column":
"cell-title cell-highlighted"
},
4:
{
"percent_column":
"cell-highlighted"
}
}
Sets CSS classes to specific grid cells
by calling removeCellCssStyles(key) followed byaddCellCssStyles(key,
hash). key is name
for this set of styles so you can reference it later - to modify it or remove
it, for example. hash is a per-row-index,
per-column-name nested hash of CSS classes to apply.
Suppose you have
a grid with columns:
["login",
"name", "birthday", "age",
"likes_icecream", "favorite_cake"]
...and you'd
like to highlight the "birthday" and "age" columns for
people whose birthday is today, in this case, rows at index 0 and 9. (The first
and tenth row in the grid).
.highlight{ background: yellow }
grid.setCellCssStyles("birthday_highlight", {
0: {
birthday: "highlight",
age: "highlight"
},
9: {
birthday: "highlight",
age: "highlight"
}
})
# Rendering
# grid.getCanvasNode()
Returns the DIV element matching
class grid-canvas, which contains every data row currently being rendered
in the DOM.
// Get the total number of data rows being rendered in
the DOM.
var numRenderedRows = $(grid.getCanvasNode()).children().length;
# grid.getGridPosition()
Returns an
object representing information about the grid's position on the page. The
object takes the form:
{
bottom:
[numPixels],
height:
[numPixels],
left:
[numPixels],
right:
[numPixels],
top:
[numPixels],
visible: [boolean],
width:
[numPixels]
}
# grid.getRenderedRange(viewportTop,
viewportLeft)
viewportTop (optional)
- The number of pixels offset from the top of the grid.
viewportLeft (optional)
- The number of pixels offset from the left of the grid.
If passed no arguments, returns an
object that tells you the range of rows (by row number) currently being
rendered, as well as the left/right range of pixels currently rendered. { top:
[rowIndex], bottom: [rowIndex], leftPx: [numPixels], rightPx: [numPixels] }
The options viewportTop and viewportLeft are
optional, and tell what what would be rendered at a certain scroll top/left
offset. For example, grid.getRenderedRange(1000) would
essentially be asking: "if I were to scroll 1000 pixels down, what rows
would be rendered?"
# grid.getViewport(viewportTop,
viewportLeft)
viewportTop (optional)
- The number of pixels offset from the top of the grid.
viewportLeft (optional)
- The number of pixels offset from the left of the grid.
Returns an object telling you which rows
are currently being displayed on the screen, and also the pixel offsets for
left/right scrolling. { top: [rowIndex], bottom: [rowIndex],
leftPx: [numPixels], rightPx: [numPixels] }
Also accepts viewportTop and viewportLeft offsets to
tell you what would be shown to the user if you were to scroll to that point.
# grid.invalidate()
Redraws the grid. Invalidates all rows
and calls render().
// Change the name property of the first row
var data = grid.getData();
data[0].name = "New
name!"
// Call invalidate to render the data again. No need to
call render, as this calls it for you.
grid.invalidate();
# grid.invalidateAllRows()
Tells the grid that all rows in the
table are invalid. (If render() is called after this, it will
redraw the entire grid.)
# grid.invalidateRow(row)
row - A row
index.
Tells the grid that the row specified
by row is invalid. (If render() is called
after this, it will redraw the contents of that row.)
# grid.invalidateRows(rows)
rows - An array
of row indices.
Accepts an array of row indices, and
tells the grid that those rows are invalid. (If render() is called
after this, it will redraw the contents of those rows.)
// Change the name property of the first row
var data = grid.getData();
data[0].name = "New
name!"
data[1].name = "Another
new name!"
// Call invalidateRows to invalidate the first two rows
grid.invalidateRows([0,1]);
// Call render to render them again
grid.render();
# grid.render()
Rerenders rows
in the DOM.
# grid.resizeCanvas()
Resizes the canvas to fit the current
DIV container. (For example, to resize the grid, you would first change the
size of the div, then call resizeCanvas().)
# grid.scrollCellIntoView(row,
cell)
row - A row
index.
cell - A column
index.
Scrolls the
indicated cell into view.
Note that this does nothing unless the
indicated column is already not in view. For example, if the grid is scrolled
to the far left and you were looking at row 0, calling scrollCellIntoView(100,0) would not
simply scroll you to row 100. But if column 8 were out of view and you calledscrollCellIntoView(100,8), then it would
scroll down and to the right.
# grid.scrollRowIntoView(row,
doPaging)
row - A row
index.
doPaging - A boolean.
If false, the grid will scroll so the indicated row is at the top
of the view. If true, the grid will scroll so the indicated
row is at the bottom of the view. Defaults to false.
Scrolls the view
to the indicated row.
# grid.scrollRowToTop(row)
row - A row
index.
Scrolls the view
to the indicated row, placing the row at the top of the view.
# grid.updateCell(row, cell)
TODO
// put stuff here
# grid.updateRow(row)
TODO
// put stuff here
# grid.updateRowCount()
TODO
// put stuff here
# Headers
# grid.getHeaderRow()
Returns the
element of a DIV row beneath the actual column headers. For an example of how
you might use this, see the header
row quick filter example, which grabs the element, appends inputs,
and delegates events to the inputs.
# grid.getHeaderRowColumn(columnId)
columnId -
The id string of a column.
If a header row
is implemented and has one child for each column, as seen in the header
row quick filter example, you may use this function to pass a
columnId and get the individual cell from that header row. Returns a DIV
element.
# grid.getSortColumns()
Returns an array
of objects representing columns that have a sort glyph in the header:
{
columnId: [string],
sortAsc:
[boolean]
}
# grid.getTopPanel()
Returns the DIV element of the top
panel. The panel is hidden by default, but you can show it by initializing the
grid with showTopPanel set to true, or by callinggrid.setTopPanelVisibility(true).
// Create a subheader and attach it to the top panel
$("<div>Here is a subheader!</div>")
.appendTo(grid.getTopPanel());
// Show the top panel
grid.setTopPanelVisibility(true);
# grid.setHeaderRowVisibility(visible)
Note: Earlier Versions used grid.showTopPanel() and grid.hideTopPanel() these have
now been replaced with grid.setTopPanelVisibility(true) andgrid.setTopPanelVisibility(false); TODO
// put stuff here
DataView
Core concepts
SlickGrid is very flexible in how it
consumes the data. The data is passed to the grid via the constructor and can
also be accessed using the setData(data) and getData() methods.
Data itself can be either an array-like object with a length property
and an indexer (data[index]) or a custom data provider implementing the following
interface:
- getLength() - returns the
number of data items in the set - getItem(index) - returns the item
at a given index - getItemMetadata(index) - returns the
metadata for the item at a given index (optional)
DataView (Slick.Data.DataView included
in slick.dataview.js) is one such data provider
and it is part of the SlickGrid package.
If all of the
data is available on the client (i.e. in a JavaScript array), DataView can
provide many useful features that the grid itself doesn't have. (This fact that
the grid lacks these features is by design - SlickGrid tries to keep the core
lean and simple while encouraging modular design and data abstraction in its
API.)
DataView works by taking in your data
and acting as a data provider that you pass to SlickGrid instead of your
original data array. For example, if you make DataView group data, it makes the
grid think that the "group" rows are just regular data items, so the
grid doesn't need to be aware of them. DataView tells the grid that those items
have a custom display and behavior and provides implementations of both. You
then wire up DataView's onRowCountChanged and onRowsChangedevents to update
the grid and voila.
Here's a rough
list of features that DataView adds to the grid:
- Paging.
- Multi-column sorting.
- Search.
- Multi-level grouping with
totals. - Expand/collapse groups.
DataView also enables very efficient
updates in response to the changing data. Whenever something changes, it
detects which rows need updating, and passes them in the onRowCountChanged andonRowsChanged events
that it fires, so instead of rerendering the entire grid, you can simply
invalidate the updated rows.
Getting started
To use the DataView, include slick.dataview.js:
// Create the DataView.
var dataView = new Slick.Data.DataView();
//Create columns
var columns = [
{id: "column1", name: "ID", field: "id"},
{id: "column2", name: "Language", field: "lang"},
{id: "column3", name: "Year", field: "year"}
];
// Pass it as a data provider to SlickGrid.
var grid = new Slick.Grid(containerEl, dataView, columns,
options);
// Make the grid respond to DataView change events.
dataView.onRowCountChanged.subscribe(function (e, args) {
grid.updateRowCount();
grid.render();
});
dataView.onRowsChanged.subscribe(function (e, args) {
grid.invalidateRows(args.rows);
grid.render();
});
Now we're ready to initialize it with
some data. One important requirement that DataView imposes on the data it
consumes is that every item has a unique identifier. DataView uses it to
uniquely identify items and compare them to each other. By default, DataView
uses the id property of the data item, but you can specify a
different one by passing it in via an optional second argument in thesetItems(data,
[objectIdProperty]). The id value must be serializable to a string.
var data = [
{'id': 'l1', 'lang': 'Java', 'year': 1995},
{'id': 'l2', 'lang': 'JavaScript', 'year': 1995},
{'id': 'l3', 'lang': 'C#', 'year': 2000},
{'id': 'l4', 'lang': 'Python', 'year': 1991}];
// This will fire the change events and update the grid.
dataView.setItems(data);
You can call getItems() to get the
data array back.
Mapping rows vs items
Ok, let's reiterate - DataView takes in
a data array as an input, manipulates it, and presents the results to the grid
by acting as a data provider, i.e. exposing the data provider methods getItem(index),getLength() and getItemMetadata(index). In handling
grid events, it's important to always keep in mind that the row indexes the
grid refers to are, in fact, the indexes into the output of the DataView! For
example, if you're handling a grid onClick event and
it's giving you a row index, to look up the data item, you need to call dataView.getItem(rowIndex) and not data[rowIndex].
In general, whenever we talk about rows,
we mean the data as the grid sees it, so, if you're using a DataView, that
would be the output of the DataView (dataView.getItem(index)). Whenever we
talk about items, we mean the input of the
DataView (data[index] or dataView.getItems()[index]). You'll notice
that it is somewhat confusing that getItem(index) returns
a row while getItems()returns items.
Unfortunately, it's this way for historical reasons. getItem(index) is part of
the data provider interface. In retrospect, it would be better if the DataView
exposed a getDataProvider()method.
DataView Methods
Since each item
has a unique id, they are often used to keep track of ids/items/rows/indices
and to map one to another.
These methods
are exposed by the DataView as part of the data provider interface:
- getItems() - Returns the
original data array. - getItem(row) - With a row index
of an item in the grid, return the item. - getItemMetadata(idx) - Returns the item
metadata at a given index. - getLength() - Returns the
number of rows in the grid.
DataView exposes
several methods in order to map ids to items to rows in the grid to indices in
the original data array:
- getItemById(id) - With an item's
id, return the item. - getIdxById(id) - With an item's
id, return the index of the item in the original data array. - getRowById(id) - With an item's
id, return the row of the item in the grid. - getItemByIdx(idx) - With an index of
an item in the original data array, return the item. Equivalent to getItems()[index]. - mapIdsToRows(idArray) - Maps an array of
item ids into rows in the grid. - mapRowsToIds(rowArray) - Maps an array of
rows in the grid into item ids.
Synchronizing selection & cell CSS
styles
One of the most
common questions about DataView is how to synchronize the selection or cell CSS
styles state on DataView changes. Let's say that the user selected a row. If
they then change the filter on the DataView to hide some items, the grid gets a
call to invalidate all changed rows, including the selected one, but it doesn't
know that the item that was displayed there has moved somewhere else. What we
need to do, is to store the ids of items that were selected, and to update the
selection on the grid any time the DataView is modified.
Luckily, there
is a helper method on the DataView that can take care of that:
- syncGridSelection(grid,
preserveHidden) -
Synchronizes grid's selected rows with the DataView by subscribing to
the grid's onSelectedRowsChanged event as well as the DataView'sonRowsChanged & onRowCountChanged events. If preserveHidden is true, it will
preserve selected items even if they are not visible as rows. For example,
if you select an item, change the DataView filter so that that item is no
longer presented to the grid and then change it back, the item will remain
selected. If preserveHidden is false, all selected items that can't be
mapped onto rows are dropped.
The
implementation is really simple, and I'll include it here for the reference:
function syncGridSelection(grid, preserveHidden) {
var self = this;
var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());;
var inHandler;
function update() {
if (selectedRowIds.length > 0) {
inHandler = true;
var selectedRows = self.mapIdsToRows(selectedRowIds);
if (!preserveHidden) {
selectedRowIds = self.mapRowsToIds(selectedRows);
}
grid.setSelectedRows(selectedRows);
inHandler = false;
}
}
grid.onSelectedRowsChanged.subscribe(function(e, args) {
if (inHandler) { return; }
selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
});
this.onRowsChanged.subscribe(update);
this.onRowCountChanged.subscribe(update);
}
NOTE: This only
works with the RowSelectionModel. CellSelectionModel isn't supported yet (I'm
open to pull requests!).
There is a
similar helper method to synchronize the cell CSS styles:
function syncGridCellCssStyles(grid, key) {
var hashById;
var inHandler;
// since this method can be called after
the cell styles have been set,
// get the existing ones right away
storeCellCssStyles(grid.getCellCssStyles(key));
function storeCellCssStyles(hash) {
hashById = {};
for (var row in hash) {
var id = rows[row][idProperty];
hashById[id] = hash[row];
}
}
function update() {
if (hashById) {
inHandler = true;
ensureRowsByIdCache();
var newHash = {};
for (var id in hashById) {
var row = rowsById[id];
if (row != undefined) {
newHash[row] = hashById[id];
}
}
grid.setCellCssStyles(key, newHash);
inHandler = false;
}
}
grid.onCellCssStylesChanged.subscribe(function(e, args) {
if (inHandler) { return; }
if (key != args.key) { return; }
if (args.hash) {
storeCellCssStyles(args.hash);
}
});
this.onRowsChanged.subscribe(update);
this.onRowCountChanged.subscribe(update);
}
Sorting
Sorting is
pretty simple:
// Subscribe to the grid's onSort event.
// It only gets fired for sortable columns, so make sure
your column definition has `sortable = true`.
grid.onSort.subscribe(function(e, args) {
// args.multiColumnSort indicates
whether or not this is a multi-column sort.
// If it is, args.sortCols will have an
array of {sortCol:..., sortAsc:...} objects.
// If not, the sort column and direction
will be in args.sortCol & args.sortAsc.
// We'll use a simple comparer function
here.
var comparer = function(a, b) {
return (a[args.sortCol.field] > b[args.sortCol.field]) ? 1 : -1;
}
// Delegate the sorting to DataView.
// This will fire the change events and
update the grid.
dataView.sort(comparer, args.sortAsc);
});
Note that calling sort() on the DataView
modifies the original data array that was passed in tosetItems(data)!
There's also a fastSort(field,
isAscending) method on
the DataView to do a fast lexicographic search that is especially handy for
older versions of IE.
Updating data
Since DataView can't automatically
detect when you're changing the data, you'll need to make these updates via the
DataView. These updates will be applied directly to the original data array.
// Delete item with id 'l3' ('C#').
dataView.deleteItem('l3');
// Append item to the end.
dataView.addItem({'id': 'l5', 'lang': 'CoffeeScript'});
// Insert item at the beginning.
dataView.insertItem(0, {'id': 'l6', 'lang': 'TypeScript'});
// Update an existing item.
var item = dataView.getItemById('l4');
item['lang'] = 'Clojure';
dataView.updateItem('l4', item);
Each one of
these updates will fire change events and the grid will get updated
automatically.
Batching updates
Notice that in
the example above, each update will result in the DataView recalculating its
data and firing change events, resulting in the grid rerendering affected rows.
If you need to make multiple changes, you should batch them up to avoid
unnecessary operations:
// Suspend recalculations until endUpdate() is called.
dataView.beginUpdate();
dataView.addItem(...);
dataView.addItem(...);
dataView.sort(...);
// Indicate that we're done updating.
dataView.endUpdate();
Filtering
Paging
Grouping
Advanced topics
API reference
Grid
Options
As included in the examples or described in stable releases:
Option |
Default |
Description |
asyncEditorLoading |
false |
Makes cell editors load asynchronously after a small |
asyncEditorLoadDelay |
100 |
Delay after which cell editor is loaded. Ignored unless |
asyncPostRenderDelay |
50 |
|
autoEdit |
true |
Cell will not automatically go into edit mode when |
autoHeight |
false |
This disables vertical scrolling. |
cellFlashingCssClass |
“flashing” |
A CSS class to apply to flashing cells via flashCell(). |
cellHighlightCssClass |
“selected” |
A CSS class to apply to cells highlighted via |
dataItemColumnValueExtractor |
null |
|
defaultColumnWidth |
80 |
|
defaultFormatter |
defaultFormatter |
|
editable |
false |
|
editCommandHandler |
queueAndExecuteCommand |
Not listed as a default under options in slick.grid.js |
editorFactory |
null |
A factory object responsible to creating an editor for |
editorLock |
Slick.GlobalEditorLock |
A Slick.EditorLock instance to use for controlling |
enableAddRow |
false |
If true, a blank row will be displayed at the bottom - |
enableAsyncPostRender |
false |
If true, async post rendering will occur and |
enableCellRangeSelection |
null |
**WARNING**: Not contained in SlickGrid 2.1, may be |
enableCellNavigation |
true |
Appears to enable cell virtualisation for optimised |
enableColumnReorder |
true |
|
enableRowReordering |
null |
**WARNING**: Not contained in SlickGrid 2.1, may be |
enableTextSelectionOnCells |
false |
|
explicitInitialization |
false |
|
forceFitColumns |
false |
Force column sizes to fit into the container |
forceSyncScrolling |
false |
|
formatterFactory |
null |
A factory object responsible to creating a formatter |
fullWidthRows |
false |
Will expand the table row divs to the full width of the |
headerRowHeight |
25 |
|
leaveSpaceForNewRows |
false |
|
multiColumnSort |
false |
|
multiSelect |
true |
|
rowHeight |
25 |
|
selectedCellCssClass |
“selected” |
|
showHeaderRow |
false |
|
syncColumnCellResize |
false |
If true, the column being resized will change its width |
topPanelHeight |
25 |
Column
Options
Options which
you can apply to the columns objects.
Option |
Default |
Description |
asyncPostRender |
null |
This accepts a function of the form function(cellNode, |
behavior |
null |
Used by the the slick.rowMoveManager.js plugin for |
cannotTriggerInsert |
null |
In the “Add New” row, determines whether clicking cells |
cssClass |
”” |
Accepts a string as a class name, applies that class to |
defaultSortAsc |
true |
When set to true, the first user click on |
editor |
null |
The editor for cell edits {TextEditor, IntegerEditor, |
field |
”” |
The property name in the data object to pull content |
focusable |
true |
When set to false, clicking on a cell in |
formatter |
null |
This accepts a function of the form function(row, |
headerCssClass |
null |
Accepts a string as a class name, applies that class to |
id |
”” |
A unique identifier for the column within the grid. |
maxWidth |
null |
Set the maximum allowable width of this column, in |
minWidth |
30 |
Set the minimum allowable width of this column, in |
name |
”” |
The text to display on the column heading. |
rerenderOnResize |
false |
If set to true, whenever this column is resized, the |
resizable |
true |
If false, column can no longer be resized. |
selectable |
true |
If false, when a row is selected, the CSS |
sortable |
false |
If true, the column will be sortable by |
toolTip |
”” |
If set to a non-empty string, a tooltip will appear on |
width |
Width of the column in pixels. (May often be overridden |
Grid
Events
SlickGrid
exposes the following events:
- onScroll ({ scrollLeft:
number, scrollTop: number }) - onSort ({
multiColumnSort: boolean, sortCol: Object, sortCols: Object[], sortAsc:
boolean }) - onHeaderContextMenu ({ column:
Object }) - onHeaderClick ({ column:
Object }) - onMouseEnter ({})
- onMouseLeave ({})
- onClick ({ row: number,
cell: number }) - onDblClick ({ row: number,
cell: number }) - onContextMenu ({})
- onKeyDown ({ row: number,
cell: number }) - onAddNewRow ({ item: any,
column: Object }) - onValidationError ({ editor:
Object, cellNode: Object, validationResult: Object, row: number, cell:
number, column: Object }) - onViewportChanged ({})
- onColumnsReordered ({})
- onColumnsResized ({})
- onCellChange ({ row: number,
cell: number, item: any }) - onBeforeEditCell ({ row: number,
cell: number, item: any, column: Object }) - onBeforeCellEditorDestroy ({ editor:
Object }) - onHeaderCellRendered ({ node: Object,
column: Object }) - onBeforeHeaderCellDestroy ({ node: Object,
column: Object }) - onBeforeDestroy ({})
- onActiveCellChanged ({ row: number,
cell: number }|null) - onActiveCellPositionChanged ({})
- onDragInit
- onDragStart
- onDrag
- onDragEnd
- onSelectedRowsChanged ({ rows:
number[] }) - onCellCssStylesChanged ({ key: string,
hash: Object })
You can
subscribe to the above events using a syntax similar to:
gridInstance.onXYZEvent.subscribe(function(e, args){
//event
handling code.
});
The handler is
called with these arguments:
e: Slick.EventData, which mimics the jQuery EventData.
args: event specific data
Event handlers
can also be removed with
gridInstance.onXYZEvent.unsubscribe(fn);
Providing
data to the grid
Overview
The data is passed to the grid via the
constructor and can also be accessed using thesetData(data) and getData() methods.
Data itself can be either an array-like object with alength property
and an indexer (data[index]) or a custom data provider implementing the following
interface:
- getLength() - returns the
number of data items in the set - getItem(index) - returns the item
at a given index - getItemMetadata(index) - returns the
metadata for the item at a given index (optional)
Item Metadata
getItemMetadata provides a
powerful way of specifying additional information about a data item that let
the grid customize the appearance and handling of a particular data item. The
method should returnnull if the item requires no special handling, or an
object in the following general format:
{
// properties
describing metadata related to the item (i.e. grid row) itself
"<property>":
value,
"<property>": value,
// properties
describing metadata related to individual columns
"columns": {
"<column index>":
{
// metadata
indexed by column index
"<property>": value,
"<property>": value
},
"<column
id>": {
// metadata
indexed by column id
"<property>": value,
"<property>": value
}
}
}
Row-level properties
- cssClasses (string) - One or
more (space-separated) CSS classes to be added to the entire row. - focusable (boolean) - Whether
or not any cells in the row can be set as "active". - selectable (boolean) - Whether
or not a row or any cells in it can be selected.
Column-level properties
- focusable (boolean) - Whether
or not a cell can be set as "active". - selectable (boolean) - Whether
or not a cell can be selected. - formatter (Function) - A
custom cell formatter. - editor (Function) - A
custom cell editor. - colspan (number|string) -
Number of columns this cell will span. Can also contain "*" to
indicate that the cell should span the rest of the row.
Order of checks
When looking up
a property, the grid checks in the following order:
- Row-level item metadata.
- Column-level item
metadata by column id. - Column-level item
metadata by column index. - Column definition.
- Grid options.
- Grid defaults.
Examples
See colspan example.