Welcome, guest ( Login )

WikiHome » DojoDataUseCases » Binding Table to Data

Binding Table to Data

Version 17, changed by chrism 02/27/2007.   Show version history


Summary: 

A complex widget, such as a FilteringTable can use property and attribute binders to the contents of a DataStore.

Comments
[Brian Skinner] I think we should design this all so that XML datastores and JSON datastores are interchangeable. The UI code, and the table binding, should be insulated from needing to know what sort of datastore they're connecting to. Which I think means we should avoid using XPath here.

[CCM] I agree, and that was the intention of the original api, but in practice, implementing general XMLStore in a path neutral-way and trying to use them behind current api has exposed that the current api needs to be more clearly specified.  In removing xpath for general XML case we delegate the mapping knowledge attribute vs. text() content to the datastore implementation.  This requires XMLStore implementations to have some knowledge of schema in order to get/set values.  For example, OPMLStore can do a straightforward mapping becuase it has implicit knowledge of it's particular schema (OPML schema) mapping for attributes--however, this is not possible in the general case XML unless schema hints are available for the XML document being accessed.  This schema info could be just an option to always map to attributes or always map to childElements in the simplest case, or could actually be implemented to use XSD/DTD info if available.
The "don't use native access path" in prop names rule needs to be documented in the api--it's currently unclear that it was designed to be path-neutral.  At the same time, it's also possible that the api could be loosened to allow use of native path to the ds if you know you're always going to work with a particular kind of data.  For example, using escape string at beginning of path, "xpath:foo.text()", or just ":foo.text()".  This supports the case where you dont care about interop, but want to make use of the power of the native datastore's path language.
Also, in terms of the P01-P03 below, I believe all three cases are valid and necessary, in the following order of importance (simple->advanced), P02, P03, P01.
ISSUE: P01 needs to be further refactored, to assume that datastore is same for all values displayed in the grid.
ISSUE: How to support different labels of columns vs. property names in P02 & P03.

Scenario:

User enters an ISBN in an input field, and presses Find button.  This executes the search against the datastore, and populates the search result items into a FilteringTable.

Programmatic:

TABLE_P01: TableGrid? with custom column binding.

// This is just the binding of data from a results set (args1.data) of a datastore.find() lookup, to the cells of a Filtering Table 'Table1'  Please see the
//  testcase for full example usage (basic app).   Note that 'path' is specific to the datastore resultset.  In this case the results are
// from an XML datastore.  Therefore path on an item in the store is represented in an 'xpath' like format.
...
<table id="Table1Table"></table>
...
var args1 = {
    oncompleted: function(result) {},
    onerror: function(error) {}
};
...
var table1 = new dijit.Grid({url:"#Table1Table?"}); // create a grid initialized on the data island
table1.columns.push(table1.createMetaData({field: "isbn", label: "ISBN"}));
table1.columns.push(table1.createMetaData({field: "title", label: "Title"}));
table1.columns.push(table1.createMetaData({field: "author", label: "Author"}));
table1.store.keyField = "isbn";
...
//var columnBindings1 = [
//    {name: "isbn", accessor: {dataStore: store1, path: "isbn/text()"}},
//    {name: "title", accessor: {dataStore: store1, path: "title/text()"}},
//    {name: "author", accessor: {dataStore: store1, path: "author/text()"}}
//];
// CM: Factor out dataStore

//var columnBindings1 = [
//    {name: "isbn", accessor: "isbn_no"}},
//    {name: "title", accessor: "title2"}},
//    {name: "author", accessor: "author"}}
//];

dojox.data.binding.bind(
    {scope: args1, event: "oncompleted"},
    {property: "[0].items", columnBindings: columnBindings1},
    {object: table1, property: "store.data"});

Assumptions:
1) Allow data property names to be different from column column names
2) Arbitrary event on a widget can be used to triggers the bind


TABLE_P02: Grid widget with custom column binding -- an alternate appraoch.

// This example (P02) tries to accomplish the same task as the example from TABLE_P01 above, 
// but using a (hopefully) simpler approach.
// This example also tries to avoid any XPath or XML-specific notation.
// And we also use a hypothetical new Grid widget instead of the old FilteringTable, so as to
// avoid having to use legacy API.
var store = new dojo.data.CsvStore({url:"books.csv"});
var grid = new dijit.Grid({datastore:store});

Assumptions:
1) A 1-1 mapping of data property names to column names, and the set of all data property names equals the set of column names (eg. the grid's columns cannot be a subset of the data...they are a direct view of the data)
2) Standard event is available on all widgets that is used to triggers the bind
3) All data values displayed in the grid originate from same datastore

TABLE_P03: Another alternative -- this time with explicit column mappings:

var store = new dojo.data.CsvStore({url:"books.csv"});
var grid = new dijit.Grid();
dojo.data.bind({
widget: grid,
datastore: store,
mapping: {columns: ["isbn", "title", "author"]}
});

Assumptions:
1) A 1-1 mapping of data property names to column names
2) Standard event is available on all widgets that is used to triggers the bind
3) All data values displayed in the grid originate from same datastore

Declarative:

TABLE_D01: TableGrid widgets with simple bindings to data islands. No need specify columns, since the widget can get that info from the data islands.
<table dojoType="TableGrid" id="widget_A1" dataSrc="#dataIsland_A"></table>
<table dojoType="TableGrid" id="widget_A2" dataSrc="#dataIsland_B"></table>
<table dojoType="TableGrid" id="widget_A3" dataSrc="#dataIsland_C"></table>
<table dojoType="TableGrid" id="widget_A4" dataSrc="#dataIsland_D"></table>
<table dojoType="TableGrid" id="widget_A5" dataSrc="#dataIsland_E"></table>
<table dojoType="TableGrid" id="widget_A6" dataSrc="#dataIsland_F"></table>
<table dojoType="TableGrid" id="widget_A7" dataSrc="#dataIsland_G"></table>
<table dojoType="TableGrid" id="widget_A8" dataSrc="#dataIsland_H"></table>

TABLE_D02: TableGrid? with simple bindings to data stores. No need specify columns, since we can get that info from the data store.

<table dojoType="TableGrid" id="widget_C1" dataStore="#dataStore_A"></table>

TABLE_D03: TableGrid? with simple bindings to data files. No need specify columns, since we can get that info from the data file.

<table dojoType="TableGrid" id="widget_D1" dataSrc="url:datafiles/booklist.csv"></table>
<table dojoType="TableGrid" id="widget_D2" dataSrc="url:datafiles/booklist.xml"></table>

TABLE_D04: TableGrid? with specific bindings that give column order, sort order, data formatting, etc.

<table dojoType="TableGrid" id="widget_E" dataSrc="#dataIsland_F">
<thead><tr>
<!-- No need to give column names, since the data store will provide them -->
<th attribute="#author" editable="true"></th>
<th attribute="#title" alignment="center"></th>
<th attribute="#pub_date" sort="asc" format="%b %d, %Y"></th>
<th attribute="#pages">NUMBER OF PAGES</th> <!-- Can explicitly give a column name -->
</tr></thead>
</table>
TABLE_D05: TableGrid with custom column binding.
See also TABLE_P01

<!--  This is just the binding of data from a results set (Args1.data) of a datastore.find() lookup, to the cells of a Filtering Table 'Table1' -->
...
<table id="Table1Table"></table>
...
var table1 = dojo.widget.createWidget("dojo:FilteringTable", {widgetId: "Table1", valueField: "isbn"}, dojo.byId("Table1Table"));
table1.columns.push(table1.createMetaData({field: "isbn", label: "ISBN"}));
table1.columns.push(table1.createMetaData({field: "title", label: "Title"}));
table1.columns.push(table1.createMetaData({field: "author", label: "Author"}));
table1.store.keyField = "isbn";
...
<div dojoType="dojox.data:XmlStore" widgetId="Store1" documentUrl="../data/bookStore.xml"></div>
<div dojoType="dojox.data:Data" widgetId="Args1"></div>
...
<div dojoType="dojox.data:TableBinding" widgetId="Binding3"
    trigger="Args1.data"
    triggerEvent="oncompleted"
    source="arguments[0].items"
    sourceStore="Store1.store"
    target="Table1.store.data">
    <div dojoType="dojox.data:ColumnBinding" widgetId="Column1"
        name="isbn"
        path="isbn/text()"></div>
    <div dojoType="dojox.data:ColumnBinding" widgetId="Column2"
        name="title"
        path="title/text()"></div>
    <div dojoType="dojox.data:ColumnBinding" widgetId="Column3"
        name="author"
        path="author/text()"></div>
</div>


TestCases:

Bind the author information from an XML datastore to a FilteringTable

Programmatic:

<html>
<head>
<title>Test Table</title>
<script>
//var djConfig = {isDebug: true};
</script>
<script src="../../../../dojo/dojo.js"></script>
<script>
dojo.require("dojo.event.*");
dojo.require("dojo.widget.*");
dojo.require("dojo.widget.FilteringTable");
dojo.registerModulePath("dojox.data", "../dojox/src/data");
dojo.require("dojox.data.binding.common");
dojo.require("dojox.data.binding.Property");
dojo.require("dojox.data.binding.Table");
dojo.require("dojox.data.binding.XmlItemAttribute");
dojo.require("dojox.data.XmlStore");
</script>
</head>
<body>
<!-- View -->
ISBN@(* for listing all): <input type="text" id="Text1" />
<input type="button" id="Button1" value="Find" />
<table id="Table1Table"></table>
<script>
var table1 = dojo.widget.createWidget("dojo:FilteringTable", {widgetId: "Table1", valueField: "isbn"}, dojo.byId("Table1Table"));
table1.columns.push(table1.createMetaData({field: "isbn", label: "ISBN"}));
table1.columns.push(table1.createMetaData({field: "title", label: "Title"}));
table1.columns.push(table1.createMetaData({field: "author", label: "Author"}));
table1.store.keyField = "isbn";
</script>

<!-- Model -->
<script>
var store1 = new ibm.data.XmlStore({documentUrl: "../data/bookStore.xml"});
var args1 = {
    oncompleted: function(result) {},
    onerror: function(error) {}
};
</script>

<!-- Controller -->
<script>
var text1 = dojo.byId("Text1");
dojox.data.binding.bind({scope: text1, event: "onblur"},
    {object: text1, property: "value"},
    {object: args1, property: "query.isbn"});

var button1 = dojo.byId("Button1");
dojo.event.connect(button1, "onclick", function() {
    store1.find(args1);
});

var columnBindings1 = [
    {name: "isbn", accessor: {dataStore: store1, path: "isbn/text()"}},
    {name: "title", accessor: {dataStore: store1, path: "title/text()"}},
    {name: "author", accessor: {dataStore: store1, path: "author/text()"}}
];
dojox.data.binding.bind({scope: args1, event: "oncompleted"},
    {property: "[0].items", columnBindings: columnBindings1},
    {object: table1, property: "store.data"});

dojo.event.connect(args1, "onerror", function(error) {
    alert(error);
});
</script>
</body>
</html>

Declarative:

<html>
<head>
<title>Test TableBinding</title>
<script>
//var djConfig = {isDebug: true};
</script>
<script src="../../../../dojo/dojo.js"></script>
<script>
dojo.require("dojo.widget.*");
dojo.require("dojo.widget.FilteringTable");
dojo.registerModulePath("dojox.data", "../dojox/src/data");
dojo.require("dojox.data.widget.Action");
dojo.require("dojox.data.widget.Binding");
dojo.require("dojox.data.widget.TableBinding");
dojo.require("dojox.data.widget.ColumnBinding");
dojo.require("dojox.data.widget.Data");
dojo.require("dojox.data.widget.XmlStore");
</script>
</head>
<body>
<!-- View -->
ISBN@(* for listing all): <input type="text" id="Text1" />
<input type="button" id="Button1" value="Find" />
Error: <input type="text" id="Text2" />
<table id="Table1Table"></table>
<script>
var table1 = dojo.widget.createWidget("dojo:FilteringTable", {widgetId: "Table1", valueField: "isbn"}, dojo.byId("Table1Table"));
table1.columns.push(table1.createMetaData({field: "isbn", label: "ISBN"}));
table1.columns.push(table1.createMetaData({field: "title", label: "Title"}));
table1.columns.push(table1.createMetaData({field: "author", label: "Author"}));
table1.store.keyField = "isbn";
</script>

<!-- Model -->
<div dojoType="dojox.data:XmlStore" widgetId="Store1" documentUrl="../data/bookStore.xml"></div>
<div dojoType="dojox.data:Data" widgetId="Args1"></div>

<!-- Controller -->
<div dojoType="dojox.data:Binding" widgetId="Binding1"
    trigger="Text1"
    triggerEvent="onblur"
    source="Text1.value"
    target="Args1.data.query.isbn"></div>

<div dojoType="dojox.data:Action" widgetId="Action1"
    trigger="Button1"
    triggerEvent="onclick"
    object="Store1"
    method="find"
    params="Args1.data"></div>

<div dojoType="dojox.data:Binding" widgetId="Binding2"
    trigger="Args1.data"
    triggerEvent="onerror"
    source="arguments[0]"
    target="Text2.value"></div>

<div dojoType="dojox.data:TableBinding" widgetId="Binding3"
    trigger="Args1.data"
    triggerEvent="oncompleted"
    source="arguments[0].items"
    sourceStore="Store1.store"
    target="Table1.store.data">
    <div dojoType="dojox.data:ColumnBinding" widgetId="Column1"
        name="isbn"
        path="isbn/text()"></div>
    <div dojoType="dojox.data:ColumnBinding" widgetId="Column2"
        name="title"
        path="title/text()"></div>
    <div dojoType="dojox.data:ColumnBinding" widgetId="Column3"
        name="author"
        path="author/text()"></div>
</div>
</body>
</html>


Discussion:







Attachment (1)

  File By Size Attached Ver.
 DataStore-to-table.jpg chrism 7K 02/20/2007 3 Delete attachment