Karsten Lehmann, Mindoo GmbH / @klehmann79
An SDK with four sections:
ExtJS / Sencha Touch
dojox/calendar (demo)
(has scalability issues)dojox/dgauges (demo)
dojox/treemap (demo)
28 new widgets:
selection dialogs, progress bars, audio/video, scrolling areas inside pages, badge icons (demo)
layoutPriority
attribute (defines sequence)design="headline", two widgets for "right" here
The same layout with design="sidebar":
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript"
src="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/dojo.js"
data-dojo-config="parseOnLoad:true"></script>
<link rel="stylesheet" type="text/css"
href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dijit/themes/claro/claro.css">
<link rel="stylesheet" type="text/css"
href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/resources/dojo.css">
<style>
html,body {
width:100%;height:100%;margin:0;overflow:hidden;
}
</style>
<script type="text/javascript">
<!-- load required widgets -->
require( ['dijit/layout/BorderContainer',
'dijit/layout/ContentPane'],
function(BorderContainer, ContentPane) {
}
);
</script>
</head>
<body class="claro">
<!-- BorderContainer -->
<div id="mainContainer" data-dojo-type="dijit/layout/BorderContainer"
style="width:100%;height:100%">
<!-- contained ContentPanes -->
<div id="topPane" data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'top'" style="height:30px">
Top pane
</div>
<div id="leftPane" data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'left',splitter:true" style="width:20%">
Left pane
</div>
<div id="centerPane" data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'center'">
center pane
</div>
<div id="rightPane1" data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'right',layoutPriority:1" style="width: 10%">
1st right pane from right
</div>
<div id="rightPane2" data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'right',layoutPriority:2" style="width: 10%">
2nd right pane from right
</div>
<div id="bottomPane" data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'bottom'">
first bottom pane
</div>
</div>
</body>
</html>
data-dojo-config
<script type="text/javascript"
src="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/dojo.js"
data-dojo-config="parseOnLoad:true"></script>
Global variable alternative:
<script type="text/javascript">
dojoConfig={
parseOnLoad: true
}
</script>
<script type="text/javascript" src="/path/to/dojo.js"></script>
data-dojo-type
data-dojo-props
(optional)<div id="topPane" data-dojo-type="dijit/layout/ContentPane"
data-dojo-props="region:'top'" style="height:30px">
Top pane
</div>
<script type="text/javascript">
<!-- load required widgets -->
require( ['dijit/layout/BorderContainer',
'dijit/layout/ContentPane'],
function(BorderContainer, ContentPane) {
//Callback with access to load classes
}
);
</script>
<script type="text/javascript">
require( ['dijit/layout/BorderContainer', 'dijit/layout/ContentPane',
'dojo/parser', 'dojo/ready'],
function(BorderContainer, ContentPane, parser, ready) {
//dojo/ready waits for DOM to load
ready(function() {
parser.parse().then(function(arrWidgets) {
//called with array of all created widgets
for (var i=0; i<arrWidgets.length; i++) {
console.log('Widget created: '+arrWidgets[i].id);
}
});
});
});
</script>
dijit/layout/ContentPane
dijit/layout/AccordionPane
Several areas below each other, one visibledijit/layout/TabContainer
Tabdojox/layout/ExpandoPane
dojox/layout/GridContainer
Portal with two portlets
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript"
src="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/dojo.js"></script>
<link rel="stylesheet" type="text/css"
href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dijit/themes/soria/soria.css">
<link rel="stylesheet" type="text/css"
href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojo/resources/dojo.css">
<link rel="stylesheet" type="text/css"
href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojox/widget/Portlet/Portlet.css">
<link rel="stylesheet" type="text/css"
href="/xsp/.ibmxspres/dojoroot-1.8.0-u/dojox/layout/resources/GridContainer.css">
<style>
html,body {width:100%;height:100%;margin:0;overflow:hidden}
.gridContainerTable {border:0;height:inherit}
.gridContainer > div {height:inherit}
</style>
<script type="text/javascript">
require( ['dijit/layout/BorderContainer',
'dijit/layout/ContentPane', 'dojox/layout/GridContainer',
'dojo/parser', 'dojo/ready',
'dojox/widget/Portlet'],
function(BorderContainer, ContentPane, GridContainer, parser,
ready, Portlet) {
ready(function() {
parser.parse();
});
});
</script>
</head>
<body class="soria">
<div id="mainContainer" data-dojo-type="dijit/layout/BorderContainer"
data-dojo-props="design:'sidebar'" style="width: 100%; height: 100%">
<!-- Portal visible in center area -->
<div id="portal" data-dojo-type="dojox/layout/GridContainer"
style="height:100%" data-dojo-props="nbZones:3,region:'center',
hasResizableColumns:true,isAutoOrganized:false,withHandles:true,
dragHandleClass:'dijitTitlePaneTitle',colWidths:[20,60,20]">
<!-- First portlet -->
<div id="portlet1" data-dojo-type="dojox/widget/Portlet"
data-dojo-props="title:'Portlet 1',column:1">
Hello World.
</div>
<!-- Second portlet -->
<div id="portlet2" data-dojo-type="dojox/widget/Portlet"
data-dojo-props="title:'Portlet 2',closable:false,column:2">
Greetings from Entwicklercamp.
</div>
</div>
</body>
</html>
3 columns, portlets movable via drag and drop
require(['dijit/registry', 'dijit/layout/ContentPane'],
function(registry, ContentPane) {
//add widgets via addChild()
//main: existing dijit/layout/BorderContainer
var mainContainer=registry.byId('main');
var newPanel=new ContentPane({
id: 'contentpane1',
content: 'Pretty dynamic content '+(new Date()).toString(),
region:'bottom',
splitter: true
});
mainContainer.addChild(newPanel);
//resize recalculates positions
mainContainer.resize();
//read contained widgets via getChildren()
var arrChildren=mainContainer.getChildren();
//remove widgets via removeChild()
var childWidget=arrChildren[0];
mainContainer.removeChild(childWidget);
//if no longer needed, free up all resources
childWidget.destroyRecursive();
}
Widgets can be placed freely, either declarative:
<body>
Lorem ipsum dolor sit amet.<br />
<input type="text" data-dojo-type="dijit/form/TextBox" /><br />
consetetur sadipscing elitr.
</body>
...or programmatic:
require(['dojo/_base/window', 'dojo/dom', 'dijit/registry'],
function(win, dom, registry) {
//get target DOM node with ID 'targetDomNodeId':
var targetDomNode=dom.byId('targetDomNodeId');
//get existing widget with ID 'myWidgetId':
var myWidget=registry.byId('myWidgetId');
//Change widget position
//(first/last=first/last child,
//before/after=before/after reference node)
myWidget.placeAt(targetDomNode, 'first');
}
});
(Please learn all of them by heart! ;-) )
Widget can be located via its ID
// dijit/registry
var widget=registry.byId('widgetId1');
Reading/writing of properties
var propValue=myWidget.get('value')
myWidget.set('value', 'xyz');
DOM node can be located via its ID
// dojo/dom
var nodeWithId=dom.byId('nodeId1')
Reading and changing styles
// dojo/dom-style
var styleValue=domStyle.get(node, 'display');
domStyle.set(node, 'backgroundColor', '#ff0000');
Reading/writing DOM node classes
// dojo/dom-class
var nodeHasClass=domClass.has(node, 'myclass');
domClass.add(node, 'myclass');
domClass.remove(node, 'myclass');
Generating and placing DOM nodes
// dojo/dom-construct
domConstruct.empty(refNode);
var createdNode=domConstruct.create('div',
{innerHTML:'<b>Test</b>'}, refNode, 'first');
domConstruct.place(refNode, otherNode, 'before');
Dimensions of DOM node
// dojo/dom-geometry
var box=domGeom.getMarginBox(node);
//box.width, box.height, box.top, box.left
Finding DOM nodes via CSS Selector
// dojo/query
var arrNodes = query('.headline', bodyNode);
Language-dependant date formatting
// dojo/date/locale
var formattedDateStr=locale.format(myDate, {selector:'date'});
Conversion between date and ISO8601 format
// dojo/date/stamp
var parsedDate=stamp.fromISOString(isoDateStr);
var dateAsString=stamp.toISOString(mydate);
Searching a value in arrays
// dojo/_base/array
var index=array.indexOf(myArray, valueToFind);
Parsing JSON strings and formatting objects as JSON
// dojo/json
var parsedJsonObj=JSON.parse("{'foo':'bar'}");
var jsonStr=JSON.stringify( {foo:'bar'} );
…and finally:
// dojo/_base/lang
// clone object recursively
var clonedObj=lang.clone(obj);
// run code in different scope
var myfunction=lang.hitch(mygrid,
function(key, value) {this.set(key, value);} );
myfunction('store', newStore);
// check for data type:
lang.isArray(x) / lang.isFunction(x) / lang.isObject(x) /
lang.isString(x)
// replacing text
lang.replace("Hello {name.first} {name.last}!",
{ name: {first: "Rudi", last: "Knegt"} });
Reacting to events
dojo.connect was divided:
"on" in the event name is cut out:
//Register event code for onClick event:
//uses dojo/dom and dojo/on
var myDomNode=dom.byId('nodeId');
var signal=on(myDomNode, 'click',
function(evt) {alert('Node clicked'); });
//unregister event code:
signal.remove();
Intercepting 'bubbling’ events in parent containers
<div id="parentDiv">
<button id="button1" class="clickMe">Click me</button>
<button id="button2" class="clickMe">Click me also</button>
<button id="button3" class="clickMe">Click me too</button>
<button id="button4" class="clickMe">Please click me</button>
</div>
<script>
require(["dojo/on", "dojo/dom", "dojo/domReady!"],
function(on, dom, domReady) {
var myObject = {
id: "myObject",
onClick: function(evt){
alert("The scope of this handler is " + this.id);
}
};
var div = dom.byId("parentDiv");
on(div, ".clickMe:click", myObject.onClick);
});
</script>
require(['dojo/aspect', 'dojo/json', 'dojo'],
function(aspect, JSON, dojo) {
//overwrite old method 'dojo.xhrGet()'
aspect.around(dojo, "xhrGet", function(originalXhr) {
return function(args) {
var t0=new Date().getTime();
//call original method
var deferred = originalXhr(args);
//we add a callback to the deferred object
deferred.then(function(data) {
var t1=new Date().getTime();
console.log("Data read in "+(t1-t0)+
"ms with request: "+JSON.stringify(args));
});
//and forward it to the caller
return deferred;
}
});
});
require(['dojo/topic'], function(topic) {
topic.subscribe("some/topic", function(event){
// process object 'event'
});
//publish new event object to all subscribers
topic.publish("some/topic", {name:"My Birthday Party",
location:"Karlsruhe"});
});
Web forms and Ajax
dijit/form/Form
simplifies working with web formsmyform.validate()
dijit/form/Form
becomes FORM tag in DOM:
<div id="myformid" data-dojo-type="dijit/form/Form">
<table>
<tr>
<td>Firstname</td>
<td><input type="text" name="firstname"
data-dojo-type="dijit/form/TextBox" /></td>
</tr>
<tr>
<td>Lastname</td>
<td><input type="text" name="lastname"
data-dojo-type="dijit/form/TextBox" /></td>
</tr>
</table>
</div>
Form data can be extracted and set:
require(['dojo/json', 'dijit/registry'],
function(JSON, registry) {
var myform=registry.byId('myformid');
var formData=myform.get('value');
//show form data as JSON string:
alert(JSON.stringify(formData));
//update form fields based on
//"name" attribute of fields
myform.set('value', {firstname:'Rudi', lastname:'Knegt'});
//validate form
if (!myform.validate())
alert('Form content invalid!');
}
});
require(["dojo/request/xhr"], function(xhr) {
xhr("myxagent.xsp", {
handleAs: "json"
method: "GET"
}).then(function(data) {
//process data (here JS object of JSON data)
}, function(err) {
//report/log error
}, function(evt){
//process progress info
//(requires browser support for XHR2)
});
});
Asynchronous Module Definition
Surely, it can’t be that many!
Activation with data-dojo-config
<script type="text/javascript" src="path/to/dojo.js"
data-dojo-config="async:true"></script>
async:false (Default)
async:true
async:false
With async:true, legacy APIs can be loaded as "dojo" module:
require(['dojo'], function(dojo) {
dojo.connect(...);
});
Defining individual AMD modules
Syntax analogous to require:
//Content of "/subdir/db.nsf/dojo/mindoo/tools/XspUtil.js":
define(['dojo/query'], function(query) {
//return simple JS object with one method
return {
//getClientId finds DOM nodes with ID ending with sComponentId
getClientId: function(sComponentId) {
var arrNodes=query("[id$='"+sComponentId+"']");
if (arrNodes && arrNodes.length>0) {
var sId=arrNodes[0].id;
return sId;
}
}
}
});
Using the defined module:
<script type="text/javascript">
dojoConfig={
packages:[
{ name:'mindoo', location:'/subdir/db.nsf/dojo/mindoo' }
]
}
</script>
<script type="text/javascript" src="path/to/dojo.js"></script>
<script type="text/javascript">
//use classname of new module in require call:
require(['mindoo/tools/XspUtil', 'dojo/domReady!'],
function(XspUtil, domReady) {
//call new defined method:
var clientId=XspUtil.getClientId('mytextfield');
alert(clientId); // view:_id1:mytextfield
});
</script>
Inheritance of classes with define:
define(['dojo/_base/declare', 'dojox/data/QueryReadStore'],
function(declare, QueryReadStore) {
//create class "mindoo/data/QueryReadStoreExt" as subclass
//of "dojox/data/QueryReadStore"
//optional 1st parameter with classname in declare() call,
//becomes global variable:
return declare("mindoo/data/QueryReadStoreExt", [QueryReadStore], {
fetch:function(request) {
request.serverQuery = {q:request.query.name};
//call superclass method:
return this.inherited("fetch", arguments);
}
});
});
The result can be loaded via require and instantiated with new.
require(['module', 'dojo/dom'],
function(module, dom) {
var moduleUri=module.uri;
//compute template path
var templatePath=moduleUri+"/../templates/mytemplate.html";
//load HTML file
require(['dojo/text!' + templatePath], function(template) {
dom.byId('content').innerHTML = template;
});
});
Parsing of template and value replacement e.g. with
Django template language dojox/dtl
Demo: Activity stream in NSF
Activating Dojo option "parseOnLoad" with the Dojo panel of the XPage:
Entering required Dojo classes in the resource tab:
Notation with " . " in R9 Beta is required here to be able to use JS Optimizer
Analog approach for fields:
Defining package location with
"Resources / Add / Dojo Module Path Resource",
then adding class as "Dojo module"
Location of Dojo file in DB Design (package explorer)
Dojo is great because:
Dojo features
Dojo features
Dojo Mobile 1.7 and 1.8
Dojo 2.0
Layouts
Dojo forms
Event handling
Drag and Drop
AMD
AMD
Special effects
Mobile Scrollpane
Alternative UI components
Custom Builds
Miscellaneous