Author | Jos de Jong, Almende B.V. |
Webpage | Chap Links Library |
License | Apache License, Version 2.0 |
Related pages:
The purpose of a DataConnector is to act as a client side proxy and caching mechanism. A DataConnector typically connects to a RESTful web service.
When a TreeGrid is connected to a DataConnector, every time it redraws it will request the data that is currently displayed from the DataConnector. The DataConnector will check if it has the data in cache, and if it is up to date. If not, it will retrieve the missing data from the server.
To connect a TreeGrid with your own RESTful web service, you will need to
create your own DataConnector which connects to your RESTful web service.
In case of readonly data, it is enough to implement the methods
getChanges
and getItems
.
When data manipulation via drag and drop is needed,
your DataConnector must also implement a set of manipulation
methods such as removeItems
and appendItems
.
There are a couple of DataConnectors provided by default,
such as the DataTable
. New data connectors can be created for
other data sources, such as a RESTful web service or a database connection.
All dataconnectors inherit from the prototype DataConnector
The example below shows how to create a dataconnector and asynchronously retrieve data. The example outputs 4 items, starting at the 20th item.
More examples can be found in the examples directory.
<!DOCTYPE HTML> <html> <head> <title>DataConnector example</title> <script type="text/javascript" src="../treegrid.js"></script> <link rel="stylesheet" type="text/css" href="../treegrid.css"> <script type="text/javascript"> // Called when the page is loaded function draw() { // generate some random data var data = []; for (var i = 0; i < 100; i++) { data.push({ '_id': i, 'name': 'Item ' + i, 'description': 'A random item' }) } var options = { // options go here... }; var table = new links.DataTable(data, options); function callback (resp) { var itemCount = resp.totalItems; var items = resp.items; document.getElementById('data').innerHTML = JSON.stringify(items, null, 2); } function errback(err) { document.getElementById('data').innerHTML = 'Error: ' + err; } // retrieve 4 items, starting at index 20 var index = 20; var num = 4; table.getItems(index, num, callback, errback); } </script> </head> <body onload="draw();"> <pre id="data"></pre> </body> </html>
The class name of the DataConnector is links.DataConnector
.
The DataConnector itself is an abstract prototype which must be implemented
by a prototype containing actual data or linking to actual data.
This is typically a DataConnector linking to a RESTful webservice.
There is a data connector DataTable
provided to handle plain JSON data.
The DataTable
can be instantiated as
var data = [ {"firstname": "John", "lastname": "Smith", "age": 15, "class": "B10"}, {"firstname": "Susan", "lastname": "Brown", "age": 16, "class": "B10"}, {"firstname": "David", "lastname": "Harris", "age": 14, "class": "B10"} ]; var options = { // options go here... }; var table = new links.DataTable(data, options);
Data can be retrieved asynchronously via:
function callback (resp) { var itemCount = resp.totalItems; var items = resp.items; console.log(items); } function errback(err) { console.log('Error: ', err); } var index = 0; var num = 10; table.getItems(index, num, callback, errback);
In the case of the DataTable the data is returned immediately via the callback function. But in most cases the data needs to be retrieved from a server, and it will take some time before the response is retrieved.
Options can be used to customize the dataconnector. Options are defined as a JSON object.
var options = { 'columns': [ {'name': 'name', 'text': 'Name', 'title': 'Name of the files'}, {'name': 'size', 'text': 'Size', 'title': 'Size of the files in kB (kilo bytes)'}, {'name': 'date', 'text': 'Date', 'title': 'Date the file is last updated'} ], 'dataTransfer' : { 'allowedEffect': 'move', 'dropEffect': 'none' } }
The following options are available.
Name | Type | Default | Description |
---|---|---|---|
columns | Object[] | undefined | Specify which fields are shown and in which order. Also specify alternative text and title for the fields. The available properties for a column are described under Columns. |
dataTransfer.allowedEffect | String | 'none' | Specify which drag operations are allowed for the items in this dataconnector. Available values are 'copy', 'move', 'link', 'copyLink', 'copyMove', 'linkMove', 'all', 'none'. |
dataTransfer.dropEffect | String | 'none' | Specify which drop operations are allowed for the items in this dataconnector. Available values are 'copy', 'move', 'link', 'none'. |
showHeader | boolean | true | Specify whether the header on top of the grid is visible. |
The option columns contains a list with objects, specifying how a column is displayed. For each column, a text, title, and custom format can be specified.
Name | Type | Required | Description |
---|---|---|---|
name | String | yes | The name of the field, corresponding with the actual field name in the items. |
text | String | no | The text being displayed in the header. |
title | String | no | Description displayed when hovering over this column header. |
width | Number | no | Fixed width for the column, in pixels. |
format | function or String | no |
A formatting function, which enables manipulation the displayed output
of a field.
Format can for example be used to format a date, address, or currency.
The function is called with the item's data as current object. Fields can
thus be accessed via this . The return value of the function
will be displayed.
An example of a format function:
|
sortable | boolean | no | If true, the column header will contain a button to sort this column. Sorting must be supported by the used DataConnector. |
For example when the items contains a firstname
and lastname
:
var data = [ {"firstname": "John", "lastname": "Smith"}, {"firstname": "Susan", "lastname": "Brown"}, {"firstname": "David", "lastname": "Harris"}, {"firstname": "Harry", "lastname": "Jones"} ];The columns for the dataconnector can be defined including a formatted column
fullname
:
var options = { "columns": [ { "name": "fullname", "text": "Full name", "format": function() { return this.firstname + ' ' + this.lastname; } }, { "name": "firstname", "text": "First name" }, { "name": "lastname", "text": "Last name" } ] };
A DataConnector contains the following methods. Most methods are asynchronously, as they are supposed to retrieve data from a server.
Method | Parameters | Return type | Description |
---|---|---|---|
appendItems (items, callback, errback) |
|
none | Append items to the data connector. |
getChanges(index, num, items, callback, errback) |
|
none |
Check for changes in a range. Provided is the range and a list with the
current version of the items.
The callback returns the changed items.
These items are not yet updated, but need to be updated via getItems .
|
getItems(index, num, callback, errback) |
|
none | Retrieve a set of items. |
getSorting() | none | Array | Retrieve the array with currently set sorting. Returns undefined if not set.
The returned array contains objects structured as: [ { field: string, order: 'asc' | 'desc' }, // ... ] |
insertItemsBefore(items, beforeItem, callback, errback) |
|
none | Insert a number of items before a given item.
If beforeItem is undefined, the items are moved to the end
of the data.
|
linkItems(sourceItems, targetItem, callback, errback) |
|
none | Link one or multiple items to another item. The meaning of this is dependent on the application |
moveItems(items, beforeItem, callback, errback) |
|
none | Move a list of items before a given item.
If beforeItem is undefined, the items are moved to the end
of the data. |
onEvent(event, params) |
|
none | The onEvent method can be overwritten for each instance
of a dataconnector, in order to catch events from this dataconnector.
Listening for events can also be achieved by registering an
event listener. |
removeItems(items, callback, errback) |
|
none | Remove items from the data connector. The removed items are returned in the callback. |
setActions(actions) |
|
none | Set a number of action icons for the data connector. The action icons will be displayed in the header of the Grid where the dataconnectors data is rendered. See section Actions for more information over the available fields. |
setSorting(sorting) |
|
Array | Set sorting order for one or multiple fields (columns).
The sorting array contains objects structured as: [ { field: string, order: 'asc' | 'desc' }, // ... ] |
updateItems(items, callback, errback) |
|
none | Update a list of items. The updated items are returned in the callback.
The passed items must contain the original objects retrieved
from the DataConnector, with changed properties. |
The asynchronous methods return a callback on success, or an errback on failure:
The DataConnector fires an event after data has been changed.
All connected visualizations will automatically update their data.
It is possible to trigger the change event via
mydataconnector.trigger('change')
,
in order to force an update of the connected visualizations.
Here an example on how to catch the change
event.
function onchange() { alert('data changed'); } google.visualization.events.addListener(mydataconnector, 'change', onchange); // Or, when using the event bus of the CHAP Links library: // links.events.addListener(mydataconnector, 'change', onchange);
The following events are available.
name | Description | Properties |
---|---|---|
change | Data has been changed. | none |
The purpose of a DataConnector is to be a client side proxy and caching mechanism for server side data. To connect a visualization to your own RESTful web service, you will have to implement your own DataConnector.
To create a DataConnector,
one has to create a javscript prototype which inherits from links.DataConnector.
DataConnector contains a number of methods such as
getChanges
and getItems
which must be overridden by
your own implementation. These methods are described under Methods.
It is not always necessary to implement all of these methods,
this depends on what the DataConnector will be used for: read-only or read/write.
Examples of DataConnector implementations can be found in:
A DataConnector implementation must inherit from links.DataConnector. Let's for example connect to a virtual RESTful API containing contacts.
/** * Contacts DataConnector */ var Contacts = function () { // 1. create an array to store cached items // 2. create a variable to hold the total number of items // 3. optionally define the columns and drag and drop support using setOptions } // set the links.DataConnector as prototype Contacts.prototype = new links.DataConnector();
When a visualization needs to redraw items, it will do this in two steps:
getChanges
,
and marks changed items as dirty.getItems
.
All methods in the dataconnector are executed asynchronously.
When finished, they will execute the the provided callback method with a single object as parameter.
This object contains parameters totalItems
and items
.
The totalItems
must be retrieved from the server.
This total should be cached so the total is not retrieved again and again.
The total number of items is sometimes provided as extra information when
retrieving some items from the server, this can save requests to the server.
Furthermore, it is also possible to set a fake count equal to the number of retrieved
items plus a fixed offset. That way, the user can always scroll down to retrieve
more items, but cannot jump in the data (which is an expensive operation
for key/value based databases).
This is demonstrated in
example04_search_youtube.html.
Checking for changes with the method getChanges
is supposed to be
fast compared to actually retrieving items with getItems
.
This method is called every time the visible window changes,
which can be multiple times per second when the user for example scrolls with the mouse in the visualization.
getChanges
does not return the new versions of the items,
but is only used to mark outdated items as dirty.
Directly after retrieving the changes, the visualization will give the dirty
items on screen a "loading" state,
to give the user feedback over what data is being loaded or refreshed.
Contacts.prototype.getChanges = function (index, num, items, callback, errback) { // 1. check if any of the given items are changed // 2. return the changed items via the callback }
The method getChanges
can be implemented in different ways.
One way is to send a request to the server to check if there are changes
in the given set of items with every call to getChanges
,
but this is not recommended as this method can be fired very often.
A better solution is to run a timer in the background to regularly check for
changes on the server, or implement some push mechanism to retrieve changes
from the server. In that way, there is no need to execute a server request
in getChanges
, but it is enough to check (client side)
whether the concering items have been replaced.
var interval = 10000; // ms var checkForUpdates = function () { // 1. retrieve changes from the server since last check // 2. if there are changes, trigger the 'change' event to let the connected // visualizations know setTimeout(checkForUpdates, interval); // set a new timeout } checkForUpdates();
When some data has been changed, the change
event can be triggered,
which will trigger all connected visualizations to update their view.
this.trigger('change');
After getting the changes,
the visualization will request the items which are either never loaded so far,
or are just marked as dirty by the response of getChanges
.
This is done with the method getItems
.
Contacts.prototype.getItems = function (index, num, callback, errback) { // 1. check if there are uncached or outdated items in the given interval // 2. if all items are cached return them via the callback // 3. else, retrieve the items from the server and return them via the callback }
When the data is already in cache and up to date, the requested items can be returned immediately. if not, the items need to be fetched from the server.
The DataConnector also supports an interface for data manipulation.
For example when drag and drop is enabled in the TreeGrid, items can be
dragged from one DataConnector and dropped in another DataConnector.
If a DataConnector needs to support drag and drop, the methods
appendItems
, insertItemsBefore
, moveItems
,
removeItems
, updateItems
,
and optionally linkItems
must be implemented.
See Methods for the exact definition of these methods.
removeItems
is called in the source dataconnector,
and appendItems
or insertItemsBefore
on the target
dataconnector. When items are moved inside the same dataconnector, the
moveItems
method is called on this dataconnector.
appendItems
or insertItemsBefore
on the
target dataconnector is called.
linkItems
on the target dataconnector is called.
After items are added or removed, the change
event needs to be triggered
to force an update of the connected visualizations.
All code and data are processed and rendered in the browser. No data is sent to any server.