500 likes | 746 Views
AMD in Dojo and beyond. By Karthick S. Agenda. Dojo fundamentals Introduction to AMD AMD Benefits AMD Development Strategies - Module vs Widget Use Case Using Dijit Widgets Creation of custom widgets Grids and Stores. JavaScript Pre-requisites. Objects Prototypal Inheritance
E N D
AMD in Dojo and beyond By Karthick S
Agenda • Dojofundamentals • Introduction to AMD • AMD Benefits • AMD Development Strategies - Module vs Widget • Use Case • Using Dijit Widgets • Creation of custom widgets • Grids and Stores
JavaScript Pre-requisites • Objects • Prototypal Inheritance • Closures • Callbacks • Anonymous functions • Prototypes • Variable Hoisting • Bind, Call and Apply
Why Dojo ? • Support for industry standard AMD across the toolkit • Field proven performance of Enterprise Web Apps • Comprehensive Dijit library • Flexible to write custom widgets • Backing of enterprise bigwigs like IBM, AOL, and Sun • Early adopters of Deferreds and Promises, Aspects, Client-side pubsub, Offline storage etc • Lots of community packages like grid, xstyle, etc
What is AMD ? • Stands for Asynchronous Module Definition • Born out of CommonJS and inspired by Dojo XHR+eval • Load ONLY the needed modules asynchronously • Also can load non-AMD plugins like HTML, JSON, etc • Supported from Dojo v1.7 onwards • https://github.com/amdjs/amdjs-api/wiki/AMD
Benefits of AMD • Say goodbye to globals - AMD provides better encapsulation • Can load and manage 3rd party packages • Loads the modules only once and caches them • Better reusability and maintainability • Baseless Dojo • Supports cross domain loading of JS modules • Better minification and portability
AMD APIs • require • define
AMD Code Structure • dojoLib/dojo • dojoLib/dijit • dojoLib/dojox • dojoLib/dgrid • dojoLib/<your-custom-package>/<your-custom-modules> • e.g.: dojoLib/myApp/myModuleOne • Dojo Library -> Package -> Module/Widget
AMD Code Structure - Contd • Dojo comes organized into packages. • Packages are collections of modules. • Modules are nothing but individual JS files with define API • Modules have 1:1 mapping with the JS files
Dojo Code Sample – Before and After AMD dojo.ready(function(){ varmyButton = dojo.byId("myButton”); Before AMD - > dojo.connect(myButton, "onclick", function(evt){ dojo.style(myDiv, "backgroundColor", "blue"); }); }); require(["dojo/on", "dojo/dom", "dojo/dom-style", "dojo/domReady!"], function(on, dom, domStyle, mouse) { var myButton = dom.byId("myButton”); After AMD - > on(myButton, "click", function(evt){ domStyle.set(myDiv, "backgroundColor", "blue"); }); });
AMD Constructs and Concepts • In the Dojo 1.6 and before, dojo.js is loaded with base API like ready, connect, style, etc. • AMD uses “base-less” dojo, i.e. none of the APIs are loaded by default. • Load them whenever we need to use them using require • Wrap the JavaScript code into modules using define • Create custom widgets using define, declare and new
Using Modules • Using require() API. • Syntax require(<module-identifiers-array>, <anonymous-callback-function>); Argument 1 - Dependencies Argument 2 - Callback Function • Example require([“myPkg/myModule”, “dojo/dom”, ”dojo/domReady!], function(myModule,dom){ myModule.initApp(); dom.byId(“testDiv”).innerHTML=“AMD is the way to go!”; });
Using Modules – contd. Module Identifier • Mappings to the path of the individual JS file(.js). • We can specify relative paths as well(like ./) • We need to specify all the dependencies here Callback Function • An anonymous function which will be called back, after loading the dependencies specified in the moduleID array. • Arguments are the returned objects of the loaded modules • The moduleIDs and the argsList should be in the same order • Special plugins like domReady! don’t return any object
Popular APIs • dojo/on - Event handling • dojo/dom - DOM manipulations • dojo/query - Advanced CSS Selectors • dojo/topic - Event publish/subscribe • dojo/aspect - Aspect oriented programming (AOP) • dijit/registry - Widgetmanipulations • dojo/_base/fx - Base effects • dojo/_base/array - Array manipulations
DojoPlugins • Extend the AMD Loader to provide special functionalities • They do not return any object when included in require • All the plugin module identifiers will end or have exclamation point (!) • Exclamation point is used to indicate process , to the loader • Popular plugins are • “dojo/text!” • “dojo/domReady!” • “dojo/il8n!”
DojoPlugins • Extend the AMD Loader to provide special functionalities • They do not return any object when included in require • All the plugin module identifiers will end or have exclamation point (!) • Exclamation point is used to indicate process , to the loader • Popular plugins are • “dojo/text!” • “dojo/domReady!” • “dojo/il8n!”
Example: dojo/text • Loads the content of the file into a string object • Replaces dojo.cache • Used to load the template HTML in widgets require([“dojo/text!./names.txt”, “dojo/domReady!”], function(names){ var names = names.split(“\n”); console.log(“Names loaded=“, names.length); });
Defining Modules • Use the define() API • Code is resolved only when needed. • Syntax define(<module-identifiers-array>, <anonymous-callback-function>); Argument 1 - Dependencies for the module Argument 2 - Factory Function - returns an object/value • Example File should -> ”<baseURL>/myPkg/myModule.js” define([“dojo/dom”], function(dom){ return { moduleName: “Test Module”, initApp: function(){ dom.byId(‘testDiv’).innerHTML = “Success”; } }; });
Dojo 1.6 and before - Using DataGrid dojo.require("dojox.grid.DataGrid"); dojo.require("dojo.data.ItemFileWriteStore"); var grid, store; dojo.ready(function(){ store = new dojo.data.ItemFileWriteStore({url: “employees.json”}); grid = new dojox.grid.DataGrid({ store: store, query: { id: "*" }, structure: [ { name: "First Name", field: "first", width: "84px" }, { name: "Last Name", field: "last", width: "84px" }, { name: “Company”, field: “company”, width: "70px" }, { name: “Department”, field: “dept”, width: "70px" }, { name: “Salary”, field: “salary”, width: "60px" } }, "grid"); grid.startup(); });
Dojo 1.7 and after - Using DataGrid var grid, dataStore, store; require(["dojox/grid/DataGrid”, "dojo/store/Memory“, "dojo/data/ObjectStore”, "dojo/_base/xhr”, "dojo/domReady!" ], function(DataGrid, Memory, ObjectStore,xhr){ xhr.get({ url: "employees.json", handleAs: "json" }).then(function(data){ store = new Memory({ data: data.items }); dataStore = new ObjectStore({ objectStore: store }); grid = new DataGrid({ store: dataStore, query: { id: "*" }, structure: [ { name: "First Name", field: "first", width: "84px" }, { name: "Last Name", field: "last", width: "84px" }, { name: “Company”, field: “company”, width: "70px" },{ name: “Department”, field: “dept”, width: "70px" }, ] }, "grid"); grid.startup(); }); });
Widget Creation • declare • Syntax Arg1 - Class/Widget name - Optional Arg2 - Subclass array - Subclasses are called Mixins in Dojo Arg3 - Class Object • Example • Without AMD dojo.provide(“myPkg.myWidget”); dojo.require(“dijit._WidgetBase”); dojo.require(“dijit._TemplatedMixin”); dojo.declare(“myPkg.myWidget”, [dijit._WidgetBase, dijit._TemplatedMixin], { templateString: dojo.cache(“myPkg”, “templates/myWidget.html”); });
Widget Creation - With AMD define([ "dojo/_base/declare", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/text!./templates/myWidget.html" ], function(declare, _WidgetBase, _TemplatedMixin, template) { return declare([_WidgetBase, _TemplatedMixin], { templateString: template }); });
WidgetLifecycle • All dojo widgets extend the base class, diiit_WidgetBase • Optionally extend dijit_templatedMixin if we need template or layout for the widget • All widgets have a set of lifecycle methods to them • These lifecycle methods are your access to write custom logic • this.inherited(arguments); - To call the superclass overridden method
WidgetLifecycleMethods • constructor - Initialization code. • parameters are mixed into the widget instance • postMixInProperties- Before DOM rendering and creation of DOM nodes. Add or change the instance properties. • buildRendering-dijit._Templated takes care.The template is fetched/read, nodes created and events hooked up during buildRendering. The end result is assigned to this.domNode. • Setters are called • postCreate-Widget has been rendered on the DOM, except for its child widgets. Sizing operations should be done here. • Startup - Called after parsing and creation of child widgets has completed. If the widget does JS sizing, then startup() should call resize(), which does the sizing. • destroy
The DijitCollection • Context, popup, and dropdown menus • Form element replacements like buttons, combo boxes, checkboxes, radio buttons, and text boxes • Date and time selection widgets • WYSIWYG Editor • Horizontal and Vertical Sliders • Progress Bars • Tabs and Accordions • Tree Structures (including Drag and Drop) • Dialogs and Tooltips • Layout widgets with slide controls and splitters
Requiring Proper Modules and Resources • It comes bundled with four supported themes: • -> nihilo, soria, tundra, and claro • Each theme contains images and CSS files CSS files must be explicitly included into each HTML page: <style type="text/css"> @import “//ajax.googleapis.com/ajax/libs/dojo/1.8/dojo/resources/dojo.css"; @import //ajax.googleapis.com/ajax/libs/dojo/1.8/dijit/themes/claro/claro.css"; </style> • <body class="claro">
dojoConfig • dojoConfig • dojoConfig = { isDebug:true, async:true } • Note: parseOnLoad was a common property in djConfig, but this is no longer recommended. See information about the parser below. <script src="//ajax.googleapis.com/ajax/libs/dojo/1.8/dojo/dojo.js" data-dojo-config="isDebug:true, async:true" type="text/javascript"></script> • <body class="claro"> • <button data-dojo-type='dijit/form/Button'>Clicky</button> • </body>
dojo/parser • We need to load dojo/parser and explicitly tell it to parse. • After the DOM is ready of course. The AMD way to determine this is with the dojo/domReady plugin. AMD is instructed that the loaded dependency is a plugin with an exclamation point, such as: dojo/domReady!. <body class="claro"> <button data-dojo-type='dijit/form/Button'>Clicky</button> <script> require([ 'dojo/parser', “dijit/form/Button”, 'dojo/domReady!' ], function(parser){ parser.parse(); // tell dojo to check the DOM for widgets }); </script> </body>
Require Dijit widgets • AMD Loader is asynchronous and smart enough to find and load un-required modules. • But Widget should be required explicitly, to get maximum performance from your application, • AMD and Dojo require that you explicitly define the modules and dependencies you want to use in your application.
Creating Widgets • The Basic select Element <select name="character" id="character"> <option value="">Select a character</option> <option value="Leonard">Dr. Leonard Leakey Hofstadter</option> <option value="Sheldon" selected="selected">Dr. Sheldon Lee Cooper</option> <option value="Rajesh">Dr. Rajesh RamayanKoothrappali</option> <option value="Howard">Howard Joel Wolowitz</option> </select> • dijit/form/FilteringSelect
Creating Widgets • Declarative way <select name="character" id="characterNode" data-dojo-type="dijit/form/FilteringSelect" data-dojo-props='autoComplete:true, pageSize:10' > <option value="">Select a character</option> <option value="Leonard">Dr. Leonard Leakey Hofstadter</option> <option value="Sheldon" selected="selected">Dr. Sheldon Lee Cooper</option> <option value="Rajesh">Dr. Rajesh RamayanKoothrappali</option> <option value="Howard">Howard Joel Wolowitz</option> </select> • When we call parser.parse(), Dojo will find this element and instantiate and initialize the widget.
Creating Widgets • Programmatic Method require([ 'dijit/form/FilteringSelect', 'dojo/domReady!' ], function(FilteringSelect){ varfilteringSelect = new FilteringSelect({ autoComplete: true, pageSize: 10 },'characterNode'); }); • dojo/parser was removed from the list of dependencies, as it’s not needed for programmatic instantiation of widgets.
Creating More Widgets require([ 'dijit/form/FilteringSelect', 'dijit/form/DateTextBox', 'dijit/form/Textbox', 'dijit/form/Textarea', 'dijit/form/Checkbox', 'dijit/form/RadioButton', 'dojo/domReady!' ], function(FilteringSelect, DateTextBox, Textbox, Textarea, Checkbox, RadioButton){ varfilteringSelect = new FilteringSelect({ autoComplete: true, pageSize: 10 },'characterNode'); var input = new Textbox({/*options*/},'myInputNode'); vartextarea = new Textarea({/*options*/},'myTextareaNode'); varmySelect = new FilteringSelect({/*options*/},'mySelectNode'); var date = new DateTextBox({/*options*/},'myDateNode'); var checkbox = new CheckBox({/*options*/},'myCheckboxNode'); var radio1 = new RadioButton({/*options*/},'myRadio1Node');});
Accessing Dijit Widgets and their Properties • Dijit has its own dijit/registry.byId method which retrieves the Dijit widget registered with the ID specified. • If the element to be made a Dijit has an ID, the widget ID will be that same value. If the source element doesn’t have an ID attribute, a widget ID will be generated. • require([ 'dojo/parser', 'dijit/registry', 'dijit/form/FilteringSelect', 'dojo/domReady!' ], function(parser, registry, FilteringSelect){ parser.parse(); varfilteringSelect = registry.byId('characterNode'); console.log('filteringSelect', filteringSelect); });
Getter and setters • If we wanted to access the pageSize property for which the Dijit widget was created from, we would access it with a Dijit getter: varpageSize = registry.byId('characterNode').get('pageSize'); // returns 10 • If we wanted to change the pageSize for the widget, we would code: registry.byId('characterNode').set('pageSize',20); //now pageSize is 20
Listening to Widget Events • Dijit widgets use dojo/on method to listen to DOM events on the given widget: • filteringSelect.on('change', function(value){ • console.log('value', value); • }); • It’s important to note that those are DOM events. If you wish to listen to a widget method, you should use dojo/aspect: require([ 'dojo/aspect' // other deps... ], function(aspect){ aspect.after(filteringSelect, 'validate', function(value){ console.log('validate', value); }); });
Dojo Object Stores • new data store API called Dojo Object Store. • This new store API is based on the HTML5 IndexedDB object store API • simplify and ease the interaction and construction of Dojo stores. • This new API follows HTTP/REST naming conventions • is compatible with the dojox.storage providers (for local storage, cookie storage, and WebSQL storage), • Separate UI from data concerns • Dojo Object Store API is an interface between different data consumers to different data producers. • Dojo core comes with two key implementations that are commonly needed: • dojo/store/Memoryand • dojo/store/JsonRest.
dojo/store/Memory • This is a very simple in-memory store. • This is highly useful for quickly creating a store, particularly for smaller datasets. • A Memory store can be created by simply providing a plain array of objects as the data source for this store, and then you can start querying and interacting with the store • The Memory store is a synchronous store, which means it directly returns values, making it very easy to use. • var product = productStore.get("slinky"); varsomeData= [ {id:1, name:"One"}, {id:2, name:"Two"} ]; store =newdojo.store.Memory({data:someData}); store.get(1) store.query({name:"One"}) store.query(function(object){ return object.id >1; }) store.put({id:3, name:"Three"}); store.remove(3);
dojo/store/JsonRest • dojo/store/JsonRest is a lightweight object store implementation of an HTTP-based (RFC 2616) client with RESTful data interaction capabilities. • This store implements the new Dojo Object Store API. • JsonRest provides full read, write, and notification capabilities through standards based HTTP/REST interaction with the server using GET, PUT, POST, and DELETE commands. • This data store allows you to communicate with server side database/persistent data storage using the Dojo Data API with JavaScript and efficiently handles create, read, update, and delete (CRUD) operations • require(["dojo/store/JsonRest"], function(JsonRestStore){ var store = new JsonRestStore({target: "/Table/" }); store.get(3).then(function(object){}); store.query("foo=bar").then(function(results){}); store.put({ foo: "bar" }, { id: 3 }); // store the object with the given identity store.remove(3); // delete the object });
Object Store API Methods • get(id) - This will trigger a GET request to {target}{id}. • query(query, options) - This will trigger a GET request to {target}{query}. If query is an object, it will be serialized using dojo.objectToQuery. If query is a string, it is appended to the URL as-is. If options includes a sort property, it will be serialized as a query parameter as well; see Sortingfor more information. • remove(id) - This will trigger a DELETE request to {target}{id}. • put(object, options) - If object includes an identity property, or options includes an id, this will trigger a PUT request to {target}{id} with the request body being the provided object serialized as JSON. If no identity is provided, then a POST request is made to the store’s target URL (no id appended) with the object as the body. If the options.incremental property is true, then a POST request is made to {target}{id} with the object as the body. You may also include an options.overwrite property. If overwrite is set to true, then an If-Match: * header is included. If overwrite is set to false, then an If-None-Match: * header is included. • add(object, options) - This behaves exactly like put(object, options), except that options.overwrite is set to false, indicating that a new object must be created.
dojo.store.Observable • dojo.store.Observable is an object store wrapper that adds support for notification of data changes to query result sets. • The query result sets returned from a Observable store will include a observe function that can be used to monitor for changes. • The observe function provides indication of the previous and new index values of changed objects to properly update result arrays. • The result sets returned from store.query() calls from a Observable store will have a observe method. The observe method has the following signature: resultSet.observe(listener); • The listener function is called with following arguments: listener(object, removedFrom, insertedInto); store = dojo.store.Observable(new dojo.store.Memory({data: someData})); varresults = store.query({rating:5}); // do something with the initial result set results.forEach(insertRow); varobserveHandle = results.observe(function(object, removedFrom, insertedInto){ if(removedFrom > -1){ // existing object removed removeRow(removedFrom); }}); store.put({rating: 5, id: 3}); store.remove(2); observeHandle.cancel();
dojo.store.Cache • dojo.store.Cache is a object store wrapper for caching data from one store in another store. This store follows the Dojo Object Store API. • The Cache store provides caching capabilities for stores. The Cache store takes two stores, a master store and caching store. • The master store is considered the data authority, all modifications go to the master store, and when data is not available in the cache, it is retrieved from the master store. • The caching store is used to record cached data from master store. Doing get() and query() call results are added to the cache (unless they don’t meet provided criteria), but only get() uses the cache, whereas query() uses the master store. • If you want to a query to come from the cache, directly call query() on the caching store. Data modifications are also mirrored in the caching store. A typical usage of dojo.store.Cache would be to use a JsonRest store as the master store, and a Memory store as the caching store. restStore = newdojo.store.JsonRest(...); memoryStore= newdojo.store.Memory(); store = newdojo.store.Cache(restStore, memoryStore); store.get(1) -> Returns the object with an id of 1 by making a GET request store.get(1) -> Returns the object, using the local memory cache store.put({id:2, name:"two"}) -> Stores the object in both master and cache store store.get(2) -> Returns the object, using the local memory cache
Introduction to the DataGrid • The DataGrid is the central component of many applications due to its effective and usable presentation of tabular data. In this tutorial we will look at how to define a grid's layout and discuss the scrolling mechanism the DataGrid uses. • dojox/grid/DataGrid • As you probably guessed, the DataGrid is made up of several different parts. • At the highest level, a DataGrid is made up of views. • Views break the DataGrid up into sections and render the headerandcontent for each section. • Headers and contents contain rows (although the header only contains one row) which are populated by sub-rows of cells. • To define how a DataGrid will look, we will be passing different objects and arrays to the structure property of the DataGrid constructor. • Cell Definition object • name: the string to use in the header cell • field: the name of the field of the data record to display • width: a string containing the CSS width (with units) of the column • hidden: a boolean that when true will hide the column
Introduction to the DataGrid • The DataGrid is the central component of many applications due to its effective and usable presentation of tabular data. In this tutorial we will look at how to define a grid's layout and discuss the scrolling mechanism the DataGrid uses. • dojox/grid/DataGrid • As you probably guessed, the DataGrid is made up of several different parts. • At the highest level, a DataGrid is made up of views. • Views break the DataGrid up into sections and render the headerandcontent for each section. • Headers and contents contain rows (although the header only contains one row) which are populated by sub-rows of cells. • To define how a DataGrid will look, we will be passing different objects and arrays to the structure property of the DataGrid constructor. • Cell Definition object • name: the string to use in the header cell • field: the name of the field of the data record to display • width: a string containing the CSS width (with units) of the column • hidden: a boolean that when true will hide the column
DataGridSubRow grid = new DataGrid({ store: store, query: { id: "*" }, structure: [ [ { name: "First Name", field: "first", width: "84px", rowSpan: 2 }, { name: "Last Name", field: "last", width: "84px", rowSpan: 2 }, { name: "Bats", field: "bats", width: "70px", rowSpan: 2 }, { name: "Throws", field: "throws", width: "70px", rowSpan: 2 }, { name: "G", field: "totalG", width: "60px" }, { name: "AB", field: "totalAB", width: "60px" }, { name: "R", field: "totalR", width: "60px" }, { name: "RBI", field: "totalRBI", width: "60px" }, { name: "BB", field: "totalBB", width: "60px" }, { name: "K", field: "totalK", width: "60px" } ],[ { name: "Games as Batter", field: "totalGAB", colSpan: 2 }, { name: "H", field: "totalH" }, { name: "2B", field: "total2B" }, { name: "3B", field: "total3B" }, { name: "HR", field: "totalHR" } ] ] }, "grid");
DataGrid Views • We've made it a little easier to view our data, however once you scroll to the right you can't see whose records you're looking at. By defining a view definition, we can lock sections of columns from scrolling left and right. A view definition is an object with some specific properties set on it: • cells: an array or an array of arrays of cell definitions • noscroll: a boolean that when true will prevent the view from scrolling horizontally • width: a string specifying the CSS width of the view — this is only needed when your cells are defined with relative widths like percentages
DataGrid Views grid = new DataGrid({ store: store, query: { id: "*" }, structure: [ { noscroll: true, cells: [ { name: "First Name", field: "first", width: "84px" }, { name: "Last Name", field: "last", width: "84px" } ] },{ cells: [ [ { name: "Bats", field: "bats", width: "70px", rowSpan: 2 }, { name: "Throws", field: "throws", width: "70px", rowSpan: 2 }, { name: "G", field: "totalG", width: "60px" }, { name: "AB", field: "totalAB", width: "60px" }, { name: "R", field: "totalR", width: "60px" }, { name: "RBI", field: "totalRBI", width: "60px" }, { name: "BB", field: "totalBB", width: "60px" }, { name: "K", field: "totalK", width: "60px" } ],[ { name: "Games as Batter", field: "totalGAB", colSpan: 2 }, { name: "H", field: "totalH" }, { name: "2B", field: "total2B" }, { name: "3B", field: "total3B" }, { name: "HR", field: "totalHR" } ] ] } ] }, "grid");
Populating your Grid using dojo/dataViews The DataGrid is the central component of many applications due to its effective and usable presentation of tabular data. In this tutorial we will look at how to populate a grid and manipulate data in a grid. • There are two ways to access the data in the store; the first is letting the DataGrid query the store for you. To do this, we can pass three parameters to the DataGrid constructor: • store: The data store. • query: The query to pass to the store. The syntax will depend on the store being used. • queryOptions: Options to pass to the store during querying. The options will depend on the store being used, and is not required. • Since we want every record, we'll pass { id: "*" }: • require([ • "dojox/grid/DataGrid", • "dojo/store/Memory", • "dojo/data/ObjectStore", • "dojo/_base/xhr", • "dojo/domReady!" • ], function(DataGrid, Memory, ObjectStore, xhr){ • var grid, dataStore; • xhr.get({ • url: "hof-batting.json", • handleAs: "json" • }).then(function(data){ • dataStore = new ObjectStore({ objectStore:new Memory({ data: data.items }) }); • grid = new DataGrid({ • store: dataStore, • query: { id: "*" }, • queryOptions: {}, • structure: [ • { name: "First Name", field: "first", width: "25%" }, • { name: "Last Name", field: "last", width: "25%" }, • { name: "G", field: "totalG", width: "10%" }, • { name: "AB", field: "totalAB", width: "10%" }, • { name: "R", field: "totalR", width: "10%" }, • { name: "H", field: "totalH", width: "10%" }, • { name: "RBI", field: "totalRBI", width: "10%" } • ] • }, "grid"); • grid.startup(); • }); • });
Populating your Grid using dojo/dataViews require([ "dojox/grid/DataGrid", "dojo/store/Memory", "dojo/data/ObjectStore", "dojo/_base/xhr", "dojo/domReady!" ], function(DataGrid, Memory, ObjectStore, xhr){ var grid, dataStore; xhr.get({ url: "hof-batting.json", handleAs: "json" }).then(function(data){ dataStore = new ObjectStore({ objectStore:new Memory({ data: data.items }) }); grid = new DataGrid({ store: dataStore, query: { id: "*" }, queryOptions: {}, structure: [ { name: "First Name", field: "first", width: "25%" }, { name: "Last Name", field: "last", width: "25%" }, { name: "G", field: "totalG", width: "10%" }, { name: "AB", field: "totalAB", width: "10%" }, { name: "R", field: "totalR", width: "10%" }, { name: "H", field: "totalH", width: "10%" }, { name: "RBI", field: "totalRBI", width: "10%" } ] }, "grid"); grid.startup(); }); });
Populating your Grid using dojo/dataViews • Formatting Data { name: "G", field: "totalG", width: "10%", formatter: function(games){ return games + " <em>games</em>"; } }, • DataGrid as a View • One very important point should be made before concluding this tutorial: the DataGrid is simply aview of a dojo/data store. • This means the DataGrid will react to changes in the store (row addition or deletion, and record field updates) if the store supports the notification API of dojo/data, and it will sort the data according to the rules of the store you're using. However, it is not designed to do sorting apart from the store. • This means that if formatting your data will change the sort order, theDataGrid won't notice the formatted changes.