|
Subject: Re: Data grid data provider discussion Newsgroups: gmane.comp.web.dojo.devel Date: 2005-12-11 06:15:45 GMT (2 years, 38 weeks, 4 days, 14 hours and 4 minutes ago) Dustin Machi wrote: > http://staff.vbi.vt.edu/dmachi/dojoDataObjectexample.html Inspired by Dustin's example code, here's another batch of example code with some more ideas for what a Dojo API might look like for things like data-providers, data-items, data-types, etc. The code below focuses mostly on the data model layer, rather than the grid widget itself, or the data provider layer. Let me know what you think... Cheers, Brian //========================================================== // Some ideas for what a Dojo API might look like for things // like data-providers, data-items, data-types, etc. // --------------------------------------------------------- // Part 1 -- It should be easy to create simple data items. var kermit = new dojo.data.Item("Kermit"); kermit.set("color", "green"); dojo.debug("Kermit is " + kermit.get("color")); var stateArray = [ { abbr: "WA", population: 5894121, name: "Washington" }, { abbr: "WV", population: 1808344, name: "West Virginia" }, { abbr: "WI", population: 5453896, name: "Wisconsin" }, { abbr: "WY", population: 493782, name: "Wyoming" } ]; var stateProvider = new dojo.data.provider.Json(stateArray); var allStates = stateProvider.fetch(); // You ask an item for the value of each of its attributes. for (var i in allStates) { var state = allStates[i]; dojo.debug(state.get("name") + " has " + state.get("population") + " people"); } var stateXML = ''; stateXML.push('<state>'); stateXML.push(' <abbr>WA</abbr>'); stateXML.push(' <name>Washington</name>'); stateXML.push('</state>'); var xmlStateProvider = new dojo.data.provider.Xml(stateXML); // --------------------------------------------------------- // Part 2 -- It should be easy to bind data items to a Grid widget. var grid = new dojo.widget.Grid(stateProvider); var column, row; var numRows = grid.getRowCount(); var numColumns = grid.getColumnCount(); var columns = grid.getColumns(); // You can access the item in a row, and the attributes of that item. for (row=0; row<numRows; row++) { var state = grid.getItemInRow(row); dojo.debug(state.get("name") + " has " + state.get("population") + " people"); } // You can access values in the grid based on row and column. var valueAtColumn1Row3 = grid.getValueAt(column = 1, row = 3); dojo.debug("grid(1, 3) == " + valueAtColumn1Row3); // Here are some operations that would change what the grid shows, // without changing any of the actual content data. grid.sort("abbr"); grid.reorderColumns(["name", "abbr", "population"]); grid.sort("population", dojo.widget.Grid.ASCENDING); grid.reorderColumns(["abbr", "population"]); grid.hideColumn("population"); grid.showRowForTotals(); grid.selectCellAt(column = 1, row = 3); // Question: Do we need to explicitly call some method like // grid.redisplay() at this point, or will the grid get // redrawn automatically? // If you get value at a specific row and column position, // the value will be different after you have sorted the // grid or changed the column order. var oldValueAtColumn1Row3 = valueAtColumn1Row3; var valueAtColumn1Row3 = grid.getValueAt(column = 1, row = 3); dojo.lang.assert(valueAtColumn1Row3 != oldValueAtColumn1Row3); // --------------------------------------------------------- // Part 3 -- Attributes are described by attribute objects. var abbr = stateProvider.getAttribute("abbr"); var name = stateProvider.getAttribute("name"); var numPeople = stateProvider.getAttribute("population"); var allAttributes = stateProvider.getAttributes(); for (var i in allAttributes) { var attribute = allAttributes[j]; dojo.debug(attribute.getToken() + " is of type " attribute.getType()); } for (var i in allStates) { var state = allStates[i]; // The argument to the item.get() method can be either // a string token or an attribute object. dojo.debug(state.get("name") + " has " + state.get("population") + " people"); dojo.debug(state.get(name) + " has " + state.get(numPeople) + " people"); } // --------------------------------------------------------- // Part 4 -- An Attribute can have a data type and other meta-data. var DATETIME = dojo.data.Types.DATETIME; var NUMBER = dojo.data.Types.NUMBER; var IMAGE = dojo.data.Types.IMAGE; var TEXT = dojo.data.Types.TEXT; // An attribute object can have an associated data type. var landArea = new dojo.data.Attribute("area", NUMBER); var stateFlag = new dojo.data.Attribute("flag", IMAGE); // An Attribute object can not only know its data type, but can also // have whatever other meta-data you want to add. For example, an // attribute might have a summary description, which could be used to // show a tooltip when the mouse hovers over the column of a grid. var admissionDate = new dojo.data.Attribute({ token: "admission", name: "Addmission into Union", type: DATETIME, summary: "This is when the state was addmitted into the US", foobar: new Date() }); var longAgo = new dojo.data.Date("1849"); var recent = new dojo.data.Date("July 2004"); var result; result = dojo.data.Date.compare(longAgo, recent); dojo.lang.assert(result == dojo.data.LESS_THAN); result = recent.compare(longAgo); dojo.lang.assert(result == dojo.data.GREATER_THAN); // --------------------------------------------------------- // Part 5 -- You can work with items individually. // An Item can be created programmatically, and its attributes // can be set. var utah = new dojo.data.Item("Utah"); utah.set("abbr", "UT"); // an alternative to: utah.set(abbr, "UT"); utah.set(numPeople, 2233169); utah.set(admissionDate, new Date("January 4, 1896")); // An Item knows what attributes it has. var attributes = utah.getAttributes(); for (var i in attributes) { var attribute = attributes[i]; var value = utah.get(attribute); dojo.debug(attribute + ": " + value); } // An Item knows what attributes it does and doesn't have. dojo.lang.assert(utah.hasAttribute(numPeople) == true); dojo.lang.assert(utah.hasAttribute(stateFlag) == false); // Items know how to describe themselves in simple formats. var jsonAboutUtah = utah.getJsonString(); dojo.debug("Everything we know about Utah: \n" + jsonAboutUtah); var xmlDescriptionOfUtah = utah.getXmlString(); dojo.debug("Everything we know about Utah: \n" + xmlDescriptionOfUtah); var vermont = new dojo.data.Item({ name: "Vermont", abbr: "VT", population: 608827 }); var populationResult, nameResult; nameResult = dojo.data.Item.compare(name, utah, vermont); populationResult = dojo.data.Item.compare(population, utah, vermont); dojo.lang.assert(nameResult == dojo.data.LESS_THAN); dojo.lang.assert(populationResult == dojo.data.GREATER_THAN); nameResult = utah.compare(name, vermont); populationResult = utah.compare(population, vermont); dojo.lang.assert(nameResult == dojo.data.LESS_THAN); dojo.lang.assert(populationResult == dojo.data.GREATER_THAN); // --------------------------------------------------------- // Part 6 -- Attributes are loosely typed, and values have types. // An attribute value can be set to a value object, rather than just a // simple string literal or number literal. var idaho = new dojo.data.Item("Idaho"); idaho.set(abbr, "ID"); idaho.set(landArea, new dojo.data.Quantity(216632, "sq km")); idaho.set(admissionDate, new dojo.data.Date("1890")); // Attributes can be loosely typed. An attribute value does not have // to match the type of of the column. var texas = new dojo.data.Item("Texas"); texas.set(abbr, "TX"); texas.set(landArea, "really really big"); texas.set(admissionDate, "don't know -- still have to look this up"); var valueObject = texas.get(landArea); var expectedType = landArea.getType(); var actualType = valueObject.getType(); dojo.lang.assert(expectedType == NUMBER); dojo.lang.assert(actualType == TEXT); dojo.lang.assert(expectedType != actualType); // This sort of loosely typed API is necessary for apps like // spreadsheets, and for data providers that connect to semi-structured // data stores, like Google Base, the Ning Content Store, the JotSpot // content store, etc. // However, some data providers only talk to structured data stores, // like relational databases. A structured data provider may not want // to pay the performance penalty for all this flexibility, so a // structured data provider may elect to have texas.get(landArea) return // a simple literal, rather than value object. Widgets with simple data // bindings will not care about the distinction. Widgets with // full-featured data bindings will have to check to see what sort of // values a provider returns. // Because all value objects implement toString(), you can always write // simple code that ignores the fact that the values are not literals dojo.lang.assert(valueObject.toString() == "really really big"); dojo.debug("The land area of Texas is: \n" + texas.get(landArea)); // --------------------------------------------------------- // Part 7 -- The same data can be bound to all sorts of widgets. var gridA = new dojo.widget.Grid(stateProvider); var gridB = new dojo.widget.Grid({ dataProvider: stateProvider, attributes: ["abbr", "name", "area", "population"] }); // match on Name var comboBoxA = new dojo.widget.ComboBox(stateProvider, "name"); // uses "name" by default var comboBoxB = new dojo.widget.ComboBox(stateProvider); var tree = new dojo.widget.Tree({ dataProvider: stateProvider, displayAttribute: "name", childAttribute: "cities" }); var chart = new dojo.widget.Chart({ plotType: "bar", dataProvider: stateProvider, xValue: "area", yAxis: "name" }); var inspector = new dojo.widget.ItemInspector(utah); // --------------------------------------------------------- // Part 8 -- Different data providers can talk to different data stores. // Dojo can offer a variety of simple data providers. var xmlProvider = new dojo.data.provider.Xml(); var jsonProvider = new dojo.data.provider.Json(); var sqlProvider = new dojo.data.provider.Sql(); var soapProvider = new dojo.data.provider.Soap(); // Third parties can implement their own data providers, for access // to proprietary content stores that hold structured data or // semi-structured data. var jotProvider = new jot.data.Provider(); var googlebaseProvider = new google.base.data.Provider(); var ningProvider = new ning.data.Provider(); var dabbleProvider = new dabble.data.Provider(); var openRecordProvider = new orp.data.Provider(); var googlemailProvider = new google.mail.data.Provider(); var availableProviders = [xmlProvider, jsonProvider, sqlProvider, soapProvider, jotProvider, googlebaseProvider, ningProvider, dabbleProvider, openRecordProvider, googlemailProvider ]; // All data providers implement a standard interface, so any of these // data providers can be used by any widget. We should be able to pick // a provider at random, and the widget won't know the difference. var provider = dojo.random.pickOneAtRandom(availableProviders); // --------------------------------------------------------- // Part 9 -- Providers don't just read, they do full Database CRUD // (Create, Read, Update, Delete) // Some simple providers will be re-only, but full-featured providers // can implement a complete read/write interface. provider.beginTransaction(); provider.delete(texas); provider.insert(utah); utah.set(name, "Utah!"); dojo.lang.assert(utah.isDirty() == true); provider.endTransaction(); dojo.lang.assert(utah.isDirty() == false); // --------------------------------------------------------- // Part 10 -- Spreadsheets can use formulas, as well as literal values. // An attribute can have a formula, which acts like a derivation rule. var peoplePerSquareKm = new dojo.data.Attribute({ token: "density", name: "Population Density", type: NUMBER, formula: "population / area" summary: "Population density is number of people per square km" }); // The derived attribute can be applied uniformly to all the items // in a grid. var grid = new dojo.widget.Grid(stateProvider); grid.showColumns(["name", "population", "area", "density"]); grid.sort("density"); // You can ask a single item for the value of a derived attribute. dojo.debug("The density of Idaho is: \n" + idaho.get("density")); // --------------------------------------------------------- // Part 11 -- A value can be an item-reference instead of a literal. var saltLakeCity = new dojo.data.Item("Salt Lake City"); // We can set the capital attribute of the utah item to point to // another item. utah.set("capital", saltLakeCity); dojo.debug("The capital of Utah is: \n" + utah.get("capital")); dojo.debug("The capital of Utah is: \n" + utah.get("capital").get("name")); // We can set up bi-directional connections between items, like the // bi-directional connects available in different content stores: // Dabble DB, OpenRecord, the Ning Content Store, etc. var author = new dojo.data.Attribute({ token: "author", name: "Author", type: ITEM, inverseAttribute: "booksAuthored" }); var booksAuthored = new dojo.data.Attribute({ token: "booksAuthored", name: "Books Authored", type: ITEM, inverseAttribute: "author" }); var jrrTolkien = new dojo.data.Item("JRR Tolkien"); var theHobbit = new dojo.data.Item("The Hobbit"); jrrTolkien.set(booksAuthored) = theHobbit; // Now theHobbit knows who its author is, even though we didn't set that // explicitly. dojo.debug('The author of "The Hobbit" is: \n' + theHobbit.get(author)); // --------------------------------------------------------- // Part 12 -- An attribute can be used by different kinds of items. // Provo and Utah can both have values for the population attributes, // even though Utah is a State, and Provo is a City. provo.set(numPeople, 105166); utah.set(numPeople, 2233169); // --------------------------------------------------------- // Part 13 -- An item can have more than one value for a single // attribute. var coloradoFlag = new dojo.data.Item("Colorado Flag"); coloradoFlag.set("color", ["red", "yellow", "white", "blue"]); var kansas = new dojo.data.Item("Kansas"); var topeka = new dojo.data.Item("Topeka"); var wichita = new dojo.data.Item("Wichita"); kansas.set(cities, [topeka, wichita, "Dodge City"]); kansas.addValue(cities, "Lawrence"); // Multi-valued attributes are common in semi-structured data stores // (Google Base, the Ning Content Store, OpenRecord, etc.), so it's // useful to have the feature available in the dojo data model. // However, many widgets may be written to assume single-valued // attributes, and widget authors may not want to have to deal with // multi-valued attributes. Likewise, for a structured content store // like a relational database, it doesn't make sense for the data // provider to support multi-valued attributes. So, all the data model // methods should support single-valued attributes as the default, and // multi-valued attributes as the exception. Hence. kermit.get("color") // only returns kermit's first color, not all colors... var kermit = new dojo.data.Item("Kermit"); kermit.set("color", ["green", "blue", "aqua"]); dojo.lang.assert(kermit.get("color") == "green"); dojo.debug("Kermit is " + kermit.get("color")); dojo.lang.assert(kermit.hasAttributeValue("color", "green") == true); dojo.lang.assert(kermit.hasAttributeValue("color", "blue") == true); dojo.lang.assert(kermit.hasAttributeValue("color", "red") == false); var arrayOfValues = kermit.getValues("color")); for (var i in arrayOfValues) { var value = arrayOfValues[i]; dojo.debug("Kermit is " + value); } // --------------------------------------------------------- // Part 14 -- A data provider can fetch items and execute at least // simple queries. var alaska = provider.fetchItem("id", "564625"); var kermit = provider.fetchItem("name", "Kermit"); var arrayOfGreenItems = provider.fetchSet("color", "green"); for (var i in arrayOfGreenItems) { var item = arrayOfGreenItems[i]; dojo.debug("Name: " + item.get("name")); } // --------------------------------------------------------- // Copyright rights relinquished under the Creative Commons // Public Domain Dedication: // http://creativecommons.org/licenses/publicdomain/ |
|
|