vegUI.org home of the javascript based window manager and AJAX framework

VegUI Tutorial - The Dataset Widget

written by vegu on 18 Apr, 2007 02:58:07
Before we get started let me state that it is very beneficial if you have read the contentbox tutorial and the list tutorial as a lot of code can be simply copied over from those tutorials to this one :).

There is also a live example of this tutorial that can be observed by following this link and any graphics that may be used in this tutorial can be downloaded here.

Okay, let's go...

As usually we start by defining and building or manager node.

code: js
Manager = new VegUIManager('Manager');
Manager.set(800,600,0,0);
Manager.T.Css.backgroundColor = '#b2b2b2';
Manager.build(document.body);

/* the collapse and expanding of the detailed field row makes use of the
* the fade effect if the effect engine was initialized. This is optional and
* if omitted no effects will be used.
*/


Manager.init_fx(50);

Now we need to create a list template that we will both use as a template for the actual dataset and the list control element that will be used for list selection within the dataset. Before we do so though let's create the css classes we will need. I will comment them so you know what they are for.

Warning, huge style sheet incoming!

code: css
/* these are the css classes used for the scroll bars and it's
* buttons
*/


.btn_scroll_left_0 { background-image: url(btn_arrow_left_0.gif); }
.btn_scroll_left_1 { background-image: url(btn_arrow_left_1.gif); }
.btn_scroll_right_0 { background-image: url(btn_arrow_right_0.gif); }
.btn_scroll_right_1 { background-image: url(btn_arrow_right_1.gif); }
.btn_scroll_down_0 { background-image: url(btn_arrow_down_0.gif); }
.btn_scroll_down_1 { background-image: url(btn_arrow_down_1.gif); }
.btn_scroll_up_0 { background-image: url(btn_arrow_up_0.gif); }
.btn_scroll_up_1 { background-image: url(btn_arrow_up_1.gif); }
.btn_scroll_drag { background-color: #c8c8c8; }
.scroll_bar { background-color: #838383; }
.blank {}

/* these are the css classes used for the content box */

.cbox_cholder {
border: 1px #838383 solid;
background-color: #fff;
}

.cbox_content {
font: bold 10px Verdana, Arial, Helvetica;
color: #838383;
}

/* these are the css classes used for the list template */

.list_normal {
font: bold 10px Verdana, Arial, Helvetica;
color: #000;
background-color: #fff;
}

.list_selected {
font: bold 10px Verdana, Arial, Helvetica;
color: #000;
background-color: #f0ead8;
}

.list_hover_selected {
font: bold 10px Verdana, Arial, Helvetica;
color: #fff;
background-color: #dfbe70;
}

.list_hover {
font: bold 10px Verdana, Arial, Helvetica;
color: #000;
background-color: #fcf9ee;
}

.list_header {
background-color: #000;
font: bold 10px Verdana, Arial, Helvetica;
color: #fff;
}

/* these are the css classes used for the dataset */

/* dataset row: normal condition */

.dset_normal {
font: bold 10px Verdana, Arial, Helvetica;
color: #000;
border-top: 1px #dfbe70 solid;
background-color: #fff;
border-bottom: none;
}

/* dataset row: selected */

.dset_selected {
font: bold 10px Verdana, Arial, Helvetica;
color: #000;
border-top: 1px #dfbe70 solid;
background-color: #f0ead8;
border-bottom: none;
}

/* dataset row: selected and hovered by mouse pointer */

.dset_hover_selected {
font: bold 10px Verdana, Arial, Helvetica;
color: #fff;
border-top: 1px #cbaa5c solid;
background-color: #dfbe70;
border-bottom: none;
}

/* dataset row: hovered by mouse pointer */

.dset_hover {
font: bold 10px Verdana, Arial, Helvetica;
color: #000;
border-top: 1px #dfbe70 solid;
background-color: #fcf9ee;
border-bottom: none;
}

/* dataset row: header row */

.dset_header {
background-color: #000;
font: bold 10px Verdana, Arial, Helvetica;
color: #fff;
}

/* dataset column: hovered by mouse pointer */

.column_hover {
border: 1px #dfbe70 solid;
background-color: #fff;
color: #000;
}

/* dataset column: normal condition */

.column_normal {
border: 1px transparent solid;
border-right: 1px #dfbe70 solid;
}

/* dataset detailed field */

.dset_detailed {
padding: 20px;
margin: 5px;
background-color: #fdfbf4;
border: 1px #f9e4b2 solid;
color: #000;
}

/* dataset panel that will hold the list element
* for input selection that will be shown when
* input is enabled for field that has list input
* as input type
*/


.dset_list_panel {
background-color: #fdfbf4;
border: 1px #dfbe70 solid;
color: #000;
}

/* dataset scaler node, used for the scale effect
* that is shown when the list panel is brought
* into view
*/


.dset_scaler {
background-color: #fdfbf4;
border: 1px #dfbe70 solid;
}

/* general input style */

input {
font: bold 10px Verdana, Arial;
}

Now let's define the list template, straight from the list tutorial copied to this tutorial :)

code: js
/* set and build the list template that will be used
* as a template for both the dataset itself and the
* list selection list within the dataset.
*
* if you do not know what is going on here i suggest
* you read the tutorial on the list widget and the
* content box widget
*/


List = Manager.get_new(VUI_LIST);

List.set(500,500,100,100,16,0);
List.CHolder.T.className = 'cbox_cholder';
List.Content.T.className = 'cbox_content';

List.ScrollY.T.className = 'scroll_bar';
List.ScrollX.T.className = 'scroll_bar';

List.ScrollY.set('y',null,null,18);
List.ScrollX.set('x',null,null,null,18);

List.ScrollY.T.rmarg_nr = -5;
List.ScrollY.T.bmarg -= 2;
List.ScrollX.T.bmarg_nr = -5;

List.ScrollY.Btn1.set(0,0,18,18,'btn_scroll_up_0','btn_scroll_up_1');
List.ScrollY.Btn2.set(0,0,18,18,'btn_scroll_down_0','btn_scroll_down_1');
List.ScrollY.Btn3.set(0,0,18,18,'btn_scroll_drag','btn_scroll_drag');

List.ScrollY.Btn3.add_skin('t',18,4,0,0,0,null,null,null,null,'img','btn_drag_y_top.gif');
List.ScrollY.Btn3.add_skin('f',18,1,0,4,0,null,3,null,null,'img','btn_drag_y_fill.gif');
List.ScrollY.Btn3.add_skin('b',18,4,0,0,0,null,null,null,-1,'img','btn_drag_y_bottom.gif');
List.ScrollY.Btn3.add_skin('p',1,1,0,0,'blank',0,0,null,null);

List.ScrollX.Btn1.set(0,0,18,18,'btn_scroll_left_0','btn_scroll_left_1');
List.ScrollX.Btn2.set(0,0,18,18,'btn_scroll_right_0','btn_scroll_right_1');
List.ScrollX.Btn3.set(0,0,18,18,'btn_scroll_drag','btn_scroll_drag');

/* set the css classes of the list */

List.set_style(
'list_hover', 'list_normal', 'list_selected', 'list_header', 'list_hover_selected'
);

/* we hide the x-axis scrollbar since we will not need it */

List.flags |= VUI_HIDE_SCROLLX;

The Dataset


Okay, now that we have the boring re-hashed stuff out of the way lets move on to dataset goodness.

We start by creating a new dataset element using the manager's get_new method.

code: js
DSet = Manager.get_new(VUI_DATASET);
DSet.clone(List);

We clone the dataset from our list template right after, however we do not want our dataset to use the same styles as our list template so we call it's set_styles() method to set new css classes.
The method if fairly similar to the set_styles() and is used the same way.
code: js
DSet.set_style(
'dset_hover','dset_normal','dset_selected','dset_header',
'dset_hover_selected','column_normal','column_hover',
'dset_detailed'
);

arg 1: css class to use when a row is 'toched' by the mouse pointer
arg 2: css class to use when a row is in default state
arg 3: css class to use when a row is selected
arg 4: css class to use for the header row
arg 5: css class to use when a selected row is 'touched' by the mouse pointer
arg 6: css class to use on a column in default state
arg 7: css class to use on a column when it is 'touched' by the mouse pointer
arg 8: css class to use on the detailed field of a row


Before we go on lets talk about the field types quickly. When setting up the dataset (we will get to that) you can define the field type of each column. The field type defines how a field in the column can be edited by the user.

There are currently four different field types that you should be familiar with from the property-set widget:


VUI_DSET_INPUT_TEXT - text input
VUI_DSET_INPUT_LIST - list input (item is chosen from a list)
VUI_DSET_INPUT_BOOL - user can select true or false for field
VUI_DSET_INPUT_CUSTOM - a custom function is used to obtain value


The user can go into edit mode on a field by double clicking the field. If text input is the field type an editable input box will be shown to the user and when the user hits the enter key the edited value will be transfered to the field.

If boolean field type is selected then double clicking the field will simply change its value from true to false or vice versa.

If list input field type is selected then a panel will brought into view that will hold a list with values and when the user selects a value from that list it is transfered to the field and the panel is hidden again.

Now, when it comes to list input there are several elements involved to make it work.

The ListTpl property of the dataset should point to the list template object that the list with the values will be cloned from. So we do that now by pointing it to our List object we defined earlier.

code: js
DSet.ListTpl = List;

The ListPanel property points to a child element of the dataset. It is the panel that will hold the value list. It is the panel that will be brought into view when a field with list input field type is
double clicked.
It is a normal and should be set up as a div element. You can also set up its position, proportions and margins to define where it will be displayed in the dataset.

code: js
DSet.ListPanel.T.className = 'dset_list_panel';

/* we set it up so that the panel will come into view
* in the center of the dataset
*/


DSet.ListPanel.set('div', 50, 50, 100, 100);
DSet.ListPanel.set_marg(100,100);

/* setting the z-index to make sure it is on top of everything else
* in the dataset
*/


DSet.ListPanel.T.z = 100;

And finally - this only matters if the effect engine has been initialized - we set up the Scaler element. The scaler element will be used to display a growing or shrinking effect when the list panel is brought into view.

In our example we simply make sure it is a div element and that it is using the right css class.

code: js
DSet.Scaler.set('div');
DSet.Scaler.T.className = 'dset_scaler';

Setting up the dataset


In order to set up the columns and field types of our dataset we do so by editing the columns template property of the dataset. Each column should be a property of the columns object and each column can have several properties as well that define it's behavior.

code: js
DSet.T.columns = {

/* first column, the name is up to you and is
* not linked to the caption that will be displayed, something
* that makes you recognize that its the first column
* might be good tho ;)
*/


column_1 : {

/* the caption of the column */

caption : 'boolean',

/* the width of the column in percent */

width : 25,

/* editType - the field type, if set will allow
* the user to edit a field in this column
*/


editType : VUI_DSET_INPUT_BOOL
},

/* second column */

column_2 : {
caption : 'text',
width : 25,
editType : VUI_DSET_INPUT_TEXT
},

/* third column */

column_3 : {
caption : 'list',
width: 25,
editType : VUI_DSET_INPUT_LIST,

/* this is the function that will be used to fill
* the list that will be shown when input mode is started
* for the third column.
*
* The argument submitted to the function is always
* pointing to the list element it is supposed to fill
*/


fill_list : function(List) {

/* clear the list */

List.flush();

/* add 100 entries to the list */

for(i = 0; i < 100; i++)
List.add_item_txt('Some Item '+i, i, true);

/* adjust the list to make sure it's scrollbars
* are working correctly
*/


List.adjust();
}
},

/* fourth column */

column_4 : {
caption : 'custom',
width : 25,

/* the custom function that will return the
* value for the field when the field is
* double clicked
*/


fetch : function() { return vui_rand(1,2500); },
editType : VUI_DSET_INPUT_CUSTOM
},

/* the detailed column, which should be hidden
* and its input type is text. The name is not optional if
* you want to set up the detailed field
*/


detailed : {
hidden : true,
editType : VUI_DSET_INPUT_TEXT
}
};

The comments should explain most of it, but i will go into detail on some of the stuff. In the third column there is a fill_list property. The column has had its field type set to list input and the fill_list property should point to a function that will fill the columns value list with values.

Again, the value list is the list that will be shown to the user to select a value for the field they double clicked.

In our example the value list is simply filled with 100 entries.

In the fourth column the field type (editType) is set to the custom and the function that will provide the value is set in the fetch property of the column. In this example it returns a random value between 1 and 2500.

If the editType property is not set on a column then the fields in the column will not be editable at all.

The last 'column' definition is for the detailed field. The hidden property assures that it will not be added as a normal column and the editType is set to text, meaning the user can also edit the detailed field by double clicking on it.

Filling the dataset


If you want to fill your dataset with data before it is built you do so by setting up the rows template property of the dataset.

Each row is an object that should provide values for each column in the dataset.

Row object example:

code: js
row = {
column_1 : true,
column_2 : 'Col 2, Row '+1,
column_3 : 1,
column_4 : 0,
detailed : 'This is some rather long text that will be displayed in the detailed field!',
id : 1
};

Note that we are using the names of the columns that we defined earlier to assign values to their fields in the row.

The detailed property set's the value of the detailed field of the row and the id property can be used to give the row a unique id. So knowing this lets fill our dataset with 10 rows.

As you see the detailed field can and is supposed to hold lots of data that wouldnt fit in any of the normal columns without being cut off. For example i use the dataset in my website control panel to manage comments to the blog. The dataset displays the posters information in the normal column and the comment in the detailed field.

code: js
var i;
for(i = 0; i < 10; i++) {
DSet.T.rows[i] = {
column_1 : true,
column_2 : 'Col 2, Row '+i,
column_3 : i,
column_4 : 0,
detailed : 'This is some rather long text that will be displayed in the detailed field!',
id : i
}
}

Alright, before we build the dataset, there are two more template properties you should know about.

The multiselect property allows you define whether more than one row can be selected at a time.

code: js
DSet.T.multiSelect = true;

The forceExpand property allows you define whether the detailed field of a row should start out expanded or collapse. By default this false and the detailed field for a row will only be shown to the user when touches the row with mouse pointer and hits the space bar on his keyboard.

code: js
DSet.T.forceExpand = true;

Now! we can build the dataset :)

code: js
Manager.build_element(DSet);

Rebuilding the dataset with changed row data


Once the dataset is built the columns and rows properties become direct properties of the dataset and are no longer template properties.

You can then change them as you need and once done you can rebuild the dataset by calling it's rebuild() method.

code: js
/* change value of second column in row 6 (starts at 0) */
DSet.rows[5].column_2 = 'Hello world!';
/* rebuild dataset
DSet.rebuild();

Letting the user delete entries


When the user has selected one or more rows and hits the DEL key on their keyboard then the delete_req() or delete_req_m() (if multiple entries are selected) are called.

In their unedited state those methods simply call the delete_item() / delete_items() method which removes the selected rows from the dataset instantly.

If you are creating network reliant application that fills the dataset with data from a database for example then you will want to send a query to the server to delete those rows and once the rows are removed successfully on the server, then and only then you want them to be removed on the client side.

The way to accomplish that is to redefine the delete_req() and delete_req_m() functions to send a query to server using the vegUI bridge and have them call the delete_item() method only when the server has given a success response.

Remember that in order to use the vegUI bridge it must have been initialized in the manager using the manager's init_bridge() method.

Untested Example:
code: js

/* the argument submitted to the function is the value (id)
* of the currently selected row
*/

DSet.delete_req = function(value) {
var d = this;
/* send request to the server for deletion */
this.Manager.Bridge.send('delete.php?v='+value,'','GET',
function() {
if(this.request.responseText == '1')
d.delete_item(value);
}
);
};

/* or for multiple items, sItems is the like the
* sItems property in the list widget and holds all
* selected rows */


DSet.delete_req_m = function(sItems) {
var d = this, i, values = [];

/* build value string */
for(i in sItems)
values.push(sItems[i].itemValue);

var vStr = values.join(',');

/* send request to the server for deletion */
this.Manager.Bridge.send('delete.php?v='+vStr,'','POST',
function() {
if(this.request.responseText == '1')
d.delete_items(sItems);
}
);
};

Let me know if there is anything that should be further explained, as i wrote this tutorial in one big session chances are i probably missed something :).

Related Posts

  • No related posts



Your Comment

name:*
email-address:

Are you human?



Comments to this post: (1)

RSS Feed for comments RSS Feed for comments
hongnk
on 01 May, 2007 - 10:48:53

I like the grid for its speed. However the custom scrollbar is not so natural (can't do page scroll). Is it possible to use windows scrollbar instead?

Report this comment