April 2, 2010

firefox setTimeout lateness argument

dijit.Dialog uses an animation when you hide it. because of this, if you want to destroy the dialog after hiding it, you have to use a setTimeout to let the animation finish before you destroy it.


setTimeout(dojo.hitch(dlg, 'destroyRecursive'), dijit.defaultDuration);


you would expect this to simply destroy the dialog and any content it contains. however, i was finding that sometimes, the domNode still remained after destroying the dialog.

after some quick debugging, i was seeing that destroyRecursive was being passed an integer as it's first parameter. destroyRecursive has an option to provide a preserveDom parameter and it will preserve the dom and so it was interpreting this integer as my intention to have the dom preserved. this was not my intention, so where was this integer coming from?

i headed straight to mozilla developer center and took a look at the docs for setTimeout. it turns out that firefox passes a lateness argument. you can see this happening with this snippet of code:


setTimeout(console.log, 100);


in my case, to make sure that the destroyRecursive function was doing what i intended it to do, i had to explicitly provide the first parameter via dojo.hitch


setTimeout(dojo.hitch(dlg, 'destroyRecursive', false), dijit.defaultDuration);

November 21, 2009

working with dijit.form.Form

i've found myself using this kind of pattern lately and found that i've also been explaining it quite often as well. so to avoid any further explanations here's some of what i've learned about how to try and design your code to leverage what dijit.form.Form (and other widgets) can provide. NOTE: my examples are based on dojo 1.3.2 and there are small variations to be made if you use dojo 1.4 (which is almost ready for release). the principles remain the same.

i'm going to cover:

  • how to set and get values for forms and form widgets

  • creating widgets to help with using complex data

  • widget templating including dojoAttachPoint and dojoAttachEvent

  • and more...



dijit setters and getters


dojo's widget library (dijit) provides a uniform way to set and get properties of widgets. this is provided by the dijit._Widget base class and it used like so:

var widget = new SomeWidget();

// get the value property of the widget
widget.attr('value');

// set the value property of the widget
widget.attr('value', 'a new value');

the .attr function makes it convenient to be able to consistently set or get properties. there's basically 3 ways that it works. i'll explain 2 of them - the 3rd way is also very powerful and makes use of the attributeMap to map widget properties to node properties.

first, .attr looks to the widget for some specially named functions to help with setting/getting properties. the naming convention is that if you're working with property foo then you can define a function called _setFooAttr which will be called in response to widget.attr('foo', value); and you can define a function called _getFooAttr which will be called in response to widget.attr('foo');

the _setFooAttr function will be passed the 2nd parameter of widget.attr('foo', value); and _getFooAttr should return a value to represent the foo property.

the 2nd option that .attr will use, is it will directly update/read the property eg. widget.attr('foo', value) is the same as widget.foo = value; and var value = widget.attr('foo'); is the same as var value = widget.foo;

just remember that if you provide the custom function to set/get then you will need to update the object property directly if you need it updated. widget.attr('foo', value); won't update widget.foo if you have _setFooAttr defined as a function in your widget. also, if you provide either of the custom functions, you are not required to provide the other one. so, for example, you might provide the setter function but not the getter function. in this case, the setter function will be used to set and the object property will be used for get.

ok, so knowing this, we can keep this in mind when we're creating widgets and leverage it as needed.

how dijit.form.Form uses setters and getters


now that we know about .attr we can look into how it's used for setting/getting values for dijit.form.Form. the value for a form is generated from the elements contained in that form. for dijit.form.Form it looks for all the widgets that are the descendants of it's containerNode and if that widget has a name property then the form value will have a property name that matches the value of the name of the widget. so, for example, if the widget's name is foo then the form's value will contain a foo property. the value of this property is obtained by getting the widget's value via widget.attr('value');

setting a dijit.form.Form value is simply the reverse of this. the form looks to the value being set and for the properties of that value, it looks for a child widget with a name to match that property and then tries to set that widget's value via widget.attr('value', formValue.widgetName); - this explanation does not follow the code exactly but the end result is the same. you should read the code if you want exact details :)

so, now that we know this, it's easy to see how if we have a dijit.form.TextBox with the name property set to firstName inside a form then we would expect the following

var form = dijit.byId('aForm'); // get a handle to the form

var value = form.attr('value'); // value.firstName will be the value of the dijit.form.TextBox

form.attr('value', {firstName: 'Bob'}); // the dijit.form.TextBox will now have the value 'Bob'


well this is easy for forms where we have a fixed number of fields and our data is 'flat' - ie, each widget just returns a string, a number, a boolean or even a Date.

what do we do though if we want to do something a little more complex? for example, we might have a form where a user can enter their name (first, last) and their address (street, city, state, zip, country). we could have a form where we have fields with names to match each of these but what if we wanted to treat the address as an object so that at the top-level, our form only has 3 properties (first, last, address)? well, we would need to have 3 widgets that have these names - first, last, address. ok, that's easy but dijit doesn't have an address widget! so... now it's time for us to write our own widget.

based on what we know about .attr and dijit.form.Form here's what our widget will have to do:

  • widget.attr('value'); will have to return an object that has properties called street, city, state, zip, country

  • widget.attr('value', formValue.address); will need to take an object with the above properties and display them appropriately

  • and our widget needs to have a name.



so, it looks like we will need to know how to build our own widgets to do this. fortunately, if you know enough about dojo to have been able to build a form with fields for street, city, state, zip and country then you're just a step or 2 away from being able to break out those fields into a widget.

well, every good tutorial has sample code, right? and i'm going to follow that trend but my code relates to a slightly more complex problem. once we work through the sample, then you should be able to come back and write the code for the problem described above.

the sample code will build a form that will allow a user to enter a schedule. so, they can choose the date for their schedule and then add tasks/events for that date. each of these tasks, will have a time and a description. the extra level of complexity that we have from the first problem (described above) is that we are going to allow the user to add or remove tasks which will mean we have a dynamic number of fields in our form. keep in mind, this problem was contrived just to demonstrate the principles in the code - it will be lacking in many ways but it serves the purpose of demonstrating the principles i'm trying to explain.

our high level design


basically, our design is driven by our data format. at the top-level, our data has 2 properties:

  • date - the date of our schedule

  • tasks - the list of tasks we have scheduled for this day



our list of tasks will be an array of objects. each of these objects will have the following properties:

  • time - the time the task begins

  • description - a description of the task



from this, we see that we need a widget for selecting a date (we'll choose dijit._Calendar) and we need a widget that can manage the list of tasks. since we can add and remove tasks from this list, then it seems like it would also be convenient to build a widget to represent a single task. this task widget will then need a widget for selecting a time (dijit.form.TimeTextBox) and another widget to enter a description (dijit.form.TextBox).

ok, so now that we have a high level design, we can start getting into the details of building these widgets. let's start with the widget for a single task and then work our way back up to the top-level of the form. you can look at the finished example if you want to see what we're working towards.

my.widgets.Task


this task widget will then need a widget for selecting a time (dijit.form.TimeTextBox) and another widget to enter a description (dijit.form.TextBox).


to keep this simple, we're going to leverage dijit's templating class - dijit._Templated. this allows us to define the high level DOM structure of our widget using html. based on our design, we can see that this should be fairly simple. the template for our Task widget is simply:

<div class='task'>
<div dojoType='dijit.form.TimeTextBox' dojoAttachPoint='timeInput'></div>
<div dojoType='dijit.form.TextBox' dojoAttachPoint='descInput'></div>
</div>


this is really simple. we have the outer div which becomes the domNode of our widget. we have put a class on this node so that we can easily style any part of this widget by including that class in our selector if necessary. this outer node has 2 children - 1 for our time widget and 1 for our description. using dojoType we can specify which widgets we want to have at the respective places in the DOM. in our javascript file that defines our widget, we just need to remember to set the widgetsInTemplate property to true so that our template gets parsed and those nodes get turned into widgets. we have also added dojoAttachPoints to our template. this allows us to map those widgets (in our template) to properties of our Task widget. by doing this, we can easily reference those individual widgets through those properties and one thing this will do is allow us to easily set or get the values of those widgets. so far, so good!

next we'll look at the javascript file which goes along with this template:

dojo.provide('my.widgets.Task');

dojo.require('dijit._Widget');
dojo.require('dijit._Templated');
dojo.require('dijit.form.TimeTextBox');
dojo.require('dijit.form.TextBox');

dojo.declare('my.widgets.Task', [dijit._Widget, dijit._Templated], {
templatePath: dojo.moduleUrl('my.widgets.templates', 'Task.html'),
widgetsInTemplate: true,

_setValueAttr: function(value){
if(value){
this.timeInput.attr('value', value.time || null);
this.descInput.attr('value', value.description || '');
} else {
this.timeInput.attr('value', null);
this.descInput.attr('value', '');
}
},

_getValueAttr: function(){
return {
time: this.timeInput.attr('value'),
description: this.descInput.attr('value')
};
}
});

ok - that's a little more code than the last block so let's break it down.

the first line is used by dojo's packaging system. this line identifies that this file provides the code for the 'my.widget.Task' package. the next few lines also relate to dojo's packaging system. they are just specifying the dependencies of this 'class'. the real meat of what we're looking at is inside dojo.declare. you can see that we are extending dijit._Widget and dijit._Templated. if you need more details about what's happening here then i'll leave it up to you to find out about this. the simple explanation here is that these are the 2 classes you most typically use to build a widget based on a template.

the next 2 lines define the path to our template and also let the templating system know that we have widgets in our template so it needs to parse the template to generate those widgets.

that leaves the 2 functions. if you can remember all the way back to the start, you'll know that these 2 functions are the custom functions used as setter/getter.

the setter checks if a parameter is passed in and sets the values of the time (this.timeInput) and description (this.descInput) widgets based on the value passed in. you can see how we're making use of the dojoAttachPoints that we indicated in our template and we're also using .attr to set the value of those widgets. this is the beauty of a standardized interface.

the getter simply forms an object with property names we're expecting (time, description) and assigns values to the properties based on the .attr getter for each of the widgets.

now that we have our Task widget, we can use it to build a list of tasks.

my.widgets.TaskList


ok, we're building on what we've learned already - no point in stopping here, we're going all the way :) i'll try to keep you a little entertained amidst all this learning.

...we need a widget that can manage the list of tasks.


this widget will manage our list of tasks - it will provide ways to:

  • add a task

  • remove a task

  • set the value from a list of tasks

  • return the list of tasks represented by this widget



add a task


we'll include a single button that will add another task with the current time and a default description of 'New Task'

remove a task


for each task that we add, we'll add a button which will provide a way for the user to remove that task

set the value from a list of tasks & return the list of tasks represented by this widget


for this, we'll write a custom setter and a custom getter - _setValueAttr and _getValueAttr

dijit._Container


since this widget will be a container for other widgets, we will mixin dijit._Container which will provide us with some functions to help managing adding/removing other widgets under our control. the only thing we really need to consider for this is that we need to designate a node as our containerNode so that these helpful functions will work :)

with all of this in mind, here's the template for this widget:

<div class='taskList'>
<div dojoAttachPoint='containerNode'></div>
<div class='add'>
<div dojoType='dijit.form.Button' dojoAttachEvent='onClick:newTask' iconClass='plusIcon'>Add Task</div>
</div>
</div>


you can see, we've got some similarities to the previous template but we also have some new features we didn't see before. i'm going to concentrate on the differences. you can see that we defined the containerNode we needed for dijit._Container using dojoAttachPoint. this is where our Task widgets will be added to when we add them.

the main feature we've used here that we didn't use previously is dojoAttachEvent. this provides a convenient way for us to connect the function of a widget in our template to a function defined in our TaskList class. the way it works, is that the string before the ':' is the name of the button's function that will be connected to our widgets function - the name on the right of the ':'. the connection is made by using the widget's connect method and this is just a convenient way to shortcut the code to do that. you can see from our template that when the onClick of the button is called, we will execute the newTask function of our widget.

now let's look at the javascript code so we can see what it looks like

dojo.provide('my.widgets.TaskList');

dojo.require('dijit._Widget');
dojo.require('dijit._Container');
dojo.require('dijit._Templated');
dojo.require('dijit.form.Button');
dojo.require('my.widgets.Task');

dojo.declare('my.widgets.TaskList', [dijit._Widget, dijit._Templated, dijit._Container], {
templatePath: dojo.moduleUrl('my.widgets.templates', 'TaskList.html'),
widgetsInTemplate: true,

name:'',
_multiValue: true,

postCreate: function(){
if(!this.getChildren().length){
// add a default 'blank' task
this.newTask();
}
},

// this creates a 'blank' new task - defaults the time to now and the description is 'New Task'
newTask: function(){
this.addTask({
time: new Date(),
description: 'New Task'
});
},

// adds a single task to our list of tasks
addTask: function(task){
new my.widgets.Task({
value: task
}).placeAt(this);
},

// sets our value - takes an array of objects
// each object should have a time and a description property
_setValueAttr: function(value){
// if we already have a value then we need to clear the widgets that represent that value
// removes all the widgets inside our 'containerNode'
this.destroyDescendants();
if(dojo.isArray(value) && value.length){
dojo.forEach(value, this.addTask, this);
} else {
// default to a 'blank' task
this.newTask();
}
},

_getValueAttr: function(){
// this will return an array of the values returned from our children via child.attr('value')
return dojo.map(this.getChildren(), "return item.attr('value');");
},

addChild: function(child){
this.inherited(arguments);
var button = new dijit.form.Button({
label: 'Remove Task',
showLabel: false,
iconClass: 'dijitEditorIcon dijitEditorIconCancel',
onClick: dojo.hitch(this, function(){
this.removeChild(child);
button.destroy();
})
}).placeAt(child.domNode);
},

removeChild: function(){
this.inherited(arguments);
if(!this.getChildren().length){
this.newTask();
}
}
});


these blocks of code are getting bigger and bigger but that's ok. we've only added a few new concepts. most of it is familiar already. let's jump right past what's already familiar and take a look at the new properties we're adding to this widget (name, _multiValue). the name property needs to be defined so that it can be picked up by the parser when we add this widget to our form. properties defined in markup will only be added by the parser to the widget if the properties are in the prototype of the widget. so we have to define name so we can use it later. also, _multiValue is a property we need to set as true whenever we will be setting/getting an array as the value of this widget.

i'm just going to summarize quickly what each of the functions do:

postCreate


just adds an empty task if no value was passed in when the widget was created

newTask


this is the function called when the user clicks the button to add a task.

addTask


this function is used to add another task to our list.

_setValueAttr


removes any previous tasks, adds each task in the list, or adds a 'blank' task if the new list is empty

_getValueAttr


returns an array which holds the results of calling the .attr('value'); getter for each child.

addChild


we're extending this function to add a button to each of our children so that when the button is clicked, the child is removed and the button is also destroyed.

removeChild


we're extending this function to add a 'blank' task to the list if we remove the last child.

ok - take some time and digest that... we have 1 final piece and that's the form. this is what we've been trying to achieve from the start... and it's taken quite a while to get here. you can see why i didn't want to keep explaining this any more!

my.widgets.App


at the top-level, our data has 2 properties:

  • date - the date of our schedule

  • tasks - the list of tasks we have scheduled for this day



it's a long way to the top if you want to rock n roll but we've finally made it! well, you already know what we're trying to achieve so here's the code:

<div class='myApp'>
<form dojoType='dijit.form.Form' dojoAttachPoint='form'>
<h1>Daily Schedule</h1>
<input dojoType='dijit._Calendar' dojoAttachEvent='onChange:onDateChange' name='date' class='dijitInline' />
<input dojoType='my.widgets.TaskList' name='tasks' class='dijitInline'/>
</form>
</div>

this time, our template contains a form which contains a heading and 2 elements. we really haven't introduced any new concepts here so since this post is already a mammoth i'll leave it for you to read it and comprehend it.

and fortunately, most of the hard work has already been done so the javascript is just shown here to complete the code:

dojo.provide('my.widgets.App');

dojo.require('dijit.form.Form');
dojo.require('dijit._Calendar');
dojo.require('dijit.form.TextBox');
dojo.require('my.widgets.TaskList');

// a little hack to get dijit._Calendar to work with the parser.
// dijit.form.Form only takes values from widgets with names and
// the parser only sees properties in the markup which already
// exist in the prototype - hence...
dojo.extend(dijit._Calendar, {
name: ''
});

dojo.declare('my.widgets.App', [dijit._Widget, dijit._Templated], {
templatePath: dojo.moduleUrl('my.widgets.templates', 'App.html'),
widgetsInTemplate: true,

// this is just going to be something to connect to so we can know when the user selects a new date
onDateChange: function(value){}
});


there's probably only 2 things which need explaining here. the dojo.extend and the empty onDateChange function.

the dojo.extend is needed so that we can give the calendar a name property in the template. if you remember i said earlier:
properties defined in markup will only be added by the parser to the widget if the properties are in the prototype of the widget.

it turns out that dijit._Calendar doesn't have the name property defined because it wasn't intended to be used like this (until dojo 1.4).

and the onDateChange function is here so that we can connect to this function and do something based on when the user selects a different date. since this post is so long... that's another post for another day :)

hopefully i've helped you get inside the 'brain of dojo' and now you can see some of the patterns that have been designed into dojo and you can leverage these patterns to work with dojo rather than fight against it or re-invent the wheel.

thanks for staying through to the end.

May 14, 2009

fetching everything selected in dojox.grid.DataGrid

the problem
the dojox.grid.DataGrid can page data and use virtual scrolling so that you can display a grid that can potentially display any of the data without needing to load all of it. the problem that we face with this is that if a user selects a row at the top of your data, quickly scrolls down towards the end of the data, holds shift and selects another row, then all the rows in between will be selected. this is fine - in fact this is desirable. however, since not all the rows in between were displayed then some of the data may not have been loaded. that is the problem. when we call
grid.selection.getSelected()
then it will return an array of items that might have some null items - these were the items that were not yet fetched.

the workaround
is an asynchronous getSelected() that takes a callback as a parameter. a warning should be given that this workaround is not thorough and maybe it should be called a hack! for me it's a proof of concept and would be a "nice to have" if dojo can provide something like this. the steps involved are straight forward
  • get the selected items in a similar manner to how it already happens with getSelected()
  • while getting those results, build an array of rows that need to be loaded
  • walk the array of unloaded items and fetch the missing pages of data
  • as each page is fetched, push the items into the original results
  • when we have all the pages, call the callback with the complete result set


here's the code pulled from a modified version of dojox.grid.DataSelection:
getSelectedAsync: function(cBack){
var result=[];
var load=[];
var requests=0;
for(var i=0, l=this.selected.length; i<l; i++){
if(this.selected[i]){
var item = this.grid.getItem(i);
item ? result.push(item) : load[i]=true;
}
}

for(var j=0, m=load.length; j<m; j++){
if(load[j]){
var pageIndex = this.grid._rowToPage(j);

requests++;
var fetchDfd = dojo.connect(this.grid, "_onFetchComplete", function(items){
dojo.disconnect(fetchDfd);
// for some reason we get called too many times...
if(requests--){
dojo.forEach(items, function(i){
result.push(i);
});
}
if(requests == 0){
cBack(result);
}
});

// request the missing page
this.grid._requestPag(pageIndex);
// skip the other items in this page so we don't have extra requests
j = this.grid._pageToRow(pageIndex+1); // -1 maybe? to account for j++ above
}
}
}

the warnings
  • this will defeat part of the purpose of the grid. the grid tries to only load pages on an "as needed" basis so that you don't have to load all of the data at one time. this goes against that idea.
  • for some reason i'm getting extra callbacks from the connect to "_onFetchComplete" and until i take the time to track them down, i would suggest that you use this at your own risk ;)
  • i've used a number of "private" functions to achieve this. that's both good and (potentially) bad. private functions are not promised to be unchanged from one release to the next but using them means that i don't have to repeat a lot of the work that is being done in these functions
i would certainly like to hear any comments about improvements to this and given a little time to live with it, i may at least search out the possibility of having something like this added to dojox.grid.DataSelection since after all DataSelection is for use with stores that implement the dojo.data api and many of them are largely asynchronous by nature.

February 7, 2009

inspiration

if you're involved in open source at all and haven't seen this, you should - especially if you're just hanging around the edges and not comfortable with diving right in. this was a great inspiration to me to get involved.

February 1, 2009

dereferencing object properties in dojox.grid.DataGrid

i'm working on a demo for Persevere and the interface is basically editable tables that represent the data that is persisted by Persevere. the editable tables are the dojox.grid.DataGrid and the demo is a primitive calendar application.

Persevere supports lazy loading and so if you have an Event object that has a calendar property to indicate which Calendar the event belongs in, that calendar is stored as a reference in the event. similarly, if the calendar has an owner that is a User object, then the owner property will reference a User. these objects that are stored as references have to be loaded before they can be referenced.

in my grid that displays the list of events, i want my columns to show:
  • Calendar - the name of the calendar that the event belongs to
  • Date - the date of the event
  • Event Name - the name/title of the event
  • Description - some extra details about the event


the way i have my Event class, this would mean that the ideal layout for my grid would be something like this:
var layout = [
{width: "20%", name: "Calendar", field: "calendar.name"},
{width: "15%", editable: true, field: "date", name: "Date", type: dojox.grid.cells.DateTextBox},
{width: "30%", editable: true, field: "name", name: "Event Name"},
{width: "35%", editable: true, field: "description", name: "Description"}
];

unfortunately, the grid (or the store - depending on where you want to post the blame) doesn't support drilling down into the item by using "calendar.name" and so the first iteration of our solution is to use a custom get function.
var getCalendar = function(row, item){
if(!item){
return this.defaultValue;
}
var store = this.grid.store;
var isItem = store && store.isItem(item, true);
if(isItem){
var calendar = store.getValue(item, "calendar");
var name = store.getValue(calendar, "name");
return name;
}
return this.defaultValue;
}
var layout = [
{width: "20%", name: "Calendar", get: getCalendar},
{width: "15%", editable: true, field: "date", name: "Date", type: dojox.grid.cells.DateTextBox},
{width: "30%", editable: true, field: "name", name: "Event Name"},
{width: "35%", editable: true, field: "description", name: "Description"}
];

this is fine for this one column but what about if this grid had more columns that i wanted to do this to and then what if i had more grids with more columns like this... that's a lot of repeating and we don't like repeating. so, the final iteration of our solution would ultimately be a subclass of dojox.grid.DataGrid that would allow us to use field names like "calendar.name"

i haven't taken this all the way through to a subclass yet but i have gone as far as overriding the default get method in the grid for each instance that i use this in.

WARNING: what i'm about to show here is only valid for reading and extra work probably needs to be done if you want to be able to edit these fields and have them write back to the store properly. if i have the need for it then i'll work on the code for writing back to the store and at that point i feel it would be worth a complete subclass and possibly a request to a dojo committer to include this as part of the grid.

var layout = [
{width: "20%", name: "Calendar", field: "calendar.name"},
{width: "15%", editable: true, field: "date", name: "Date", type: dojox.grid.cells.DateTextBox},
{width: "30%", editable: true, field: "name", name: "Event Name"},
{width: "35%", editable: true, field: "description", name: "Description"}
];
var grid = new dojox.grid.DataGrid({
structure: layout,
region: 'center',
store: this.store,
get: function(rowIndex, inItem){
var grid = this.grid;
var dereference = function(item, field){
var props = field.concat().split('.');
var val = item;
dojo.forEach(props, function(prop){
val = grid.store.getValue(val, prop);
});
return val;
}
return (!inItem ? this.defaultValue : (!this.field ? this.value : dereference(inItem, this.field)));
}
}, dojo.doc.createElement('div')).placeAt(this);


this code is taken from a custom widget that is a subclass of dijit.layout.BorderContainer that i'm using so take that into consideration when looking at lines like the last line of code that places the grid at this.

something worth noting is that inside the get function, the context is a cell.

to conclude, i'm certainly looking for any improvements to this and help with writing back to the store properly would be appreciated too. ultimately, i would like this functionality to be included in the grid.

January 15, 2009

dojo.data.ItemFileWriteStore

when starting to use the dojo data API, the first store that most people learn to use is the dojo.data.ItemFileReadStore (read only) and it's counterpart dojo.data.ItemFileWriteStore (read & write). out of the box, dojo.data.ItemFileWriteStore (IFWS) doesn't actually have a mechanism to write anything anywhere. everything is in place to write, but the functions that actually do the writing are essentially a no-op and so you have to write your own. this is something that is not clearly obvious when you start to use IFWS and so it needs to be clearly stated. also, here is a simple example of writing to your server URL. NOTE: IFWS is not a very efficient store since ALL the data is written each time the store is saved.

the complete example is available here

here's the javascript:

dojo.require("dojox.grid.DataGrid");
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dijit.form.Button");

var fillStore = function(){
dojo.xhrGet({
url: "samfuqua.json",
handleAs: "json",
load: function(data, ioArgs){
dijit.byId('table').setStore(new dojo.data.ItemFileWriteStore({
data: data
}));
dijit.byId('table').store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){
console.log(newFileContentString);
dojo.xhrPost({
url: "yourURL",
postData: dojo.toJson(dojo.fromJson(newFileContentString)), // this unprettifies the string
error: saveFailedCallback,
load: saveCompleteCallback
});
}
}
});
}

dojo.addOnLoad(fillStore);


and here's the html:

<body class="tundra">
<button dojoType="dijit.form.Button" onclick="dijit.byId('table').store.revert();">Revert</button>
<button dojoType="dijit.form.Button" onclick="dijit.byId('table').store.save();">Save</button>
<table id="table" dojoType="dojox.grid.DataGrid">
<thead>
<tr>
<th field="genre">Genre</th>
<th field="author" editable=true>Author</th>
<th field="title">Title</th>
<th field="price">Price</th>
</tr>
</thead>
</table>
</body>


the part to focus in on is this section here:

dijit.byId('table').store._saveEverything = function(saveCompleteCallback, saveFailedCallback, newFileContentString){
console.log(newFileContentString);
dojo.xhrPost({
url: "yourURL",
postData: dojo.toJson(dojo.fromJson(newFileContentString)), // this unprettifies the string
error: saveFailedCallback,
load: saveCompleteCallback
});
}


for IFWS to actually do something, you have to override either the _saveEverything or the _saveCustom functions. in this case, we are overriding _saveEverything. when IFWS calls the _saveEverything function it will pass a saveCompleteCallback, saveFailedCallback and a newFileContentString. conveniently, these are basically what we would need to have to make an xhrPost to our server :)

the xhrPost is fairly straightforward, you provide a url, some data, an error callback and a load (success) callback. the only manipulation we've had to do here is "unprettify" the newFileContentString. when newFileContentString is passed in, it is a "prettified" json string - ie it has tabs, and newlines, etc. to format the string so that it looks "pretty" when it's printed. this is nice if you're writing the text directly to a file but this can cause problems if your server needs to understand the data so to unprettify it, i've converted the json to an object and then back to json again without prettifying it.

this is just a quick look at IFWS and for a more complete look at it, take a look at the docs at dojocampus

November 12, 2008

multiple versions of dojo

so, you've inherited a project and it uses an old version of dojo and you really don't have the time to get up to speed with the changes that have happened in dojo since that release and your project ain't broke so you don't really want to fix it but somebody wants a new feature or noticed a small defect and even if this scenario doesn't completely match your circumstances... for some reason you have a need to be running 2 versions of dojo at the same time. if this is the case then keep reading...

first of all, for the attention impaired, here's a link to what we're working towards http://archive.dojotoolkit.org/nightly/dojotoolkit/dojo/tests/_base/_loader/scope/scope04.html. this page shows dojo 0.4.3 from the aol cdn working beside the latest nightly build of dojo.

i recently helped someone who had a project that used dojo 0.4.3 and according to them, on FF3 the initial dialog markup was being displayed as the page was loading even though there was inline styling that was supposed to keep them hidden. i never confirmed this for myself but that is what they were telling me. in this case, they didn't want to migrate their whole project but they just wanted to fix their dialogs. so, it seemed that the path of least resistance was to run 2 versions of dojo side-by-side and leave most of the code alone and just migrate the dialogs.

the book of dojo already has a section about multiple versions of dojo in a page but it doesn't hurt to have another perspective on it. so let's dig into this. the basic idea is leave the old stuff alone and bring in the new stuff in a way that the old stuff is unaware of.

i'll start at the top of the file and work down explaining each part - css, javascript and html.

css
this file links to 3 different style sheets and the first one and the third one are related to the nightly build version of dojo and the 2nd one is specifically related to the dijit tests.

<link rel="stylesheet" type="text/css" href="../../../../resources/dojo.css">
<link rel="stylesheet" type="text/css" href="../../../../../dijit/tests/css/dijitTests.css">
<link rel="stylesheet" type="text/css" href="../../../../../dijit/themes/tundra/tundra.css">

for the guy i was helping, he didn't want any changes to the styling he already had so the only css he had to add was:

<link rel="stylesheet" type="text/css" href="dojoroot/dijit/themes/dijit.css">

this file has the essential styles for the dialog (and other widgets) to function properly.

javascript
since the ability to use multiple versions of dojo was only introduced in 1.1, if we are using a version that is pre 1.1 then we want to load the old version of dojo first. in theory, if both versions you want to use are 1.1 or later then it may be possible to load either version first. the important thing is the one which is loaded without using custom namespaces will be the one that gets the "dojo" namespace (and "dijit" and "dojox" if that version used them). i would recommend that if both versions are 1.1 or later then it would be a good practice to give each one it's own custom namespace so that there is no confusion about which version of dojo you are referring to.

loading the old version of dojo
you shouldn't need to change anything here if you're working with a pre-existing project. if you don't have djConfig defined then there should be no need to define it at this point.

<script type="text/javascript">
//djConfig for 0.4.3 setup.
djConfig = {
isDebug: true
};
</script>
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.4.3/dojo.js"></script>

loading the new version of dojo
now that the old version has used djConfig and loaded itself, it will be safe for us to make changes to djConfig and load in our 2nd version of dojo - making sure to use a custom namespace for this version. the custom namespace is achieved through djConfig.scopeMap. this tells dojo how to map it's namespaces. in the example below "dojo" maps to "dojo10", "dijit" maps to "dijit10" and "dojox" maps to "dojox10". it is worthwhile noting that djConfig.baseUrl should point to the directory containing the dojo.js file - this is not quite obvious from this example.

<script type="text/javascript">
//Need scope map defined in a script block. It will not work as part of the
//djConfig attribute on the script that loads Dojo.
//Also, just adding properties instead of redefining djConfig, since that
//will wipe out djConfig values set up by the 0.4.3 dojo.
djConfig.parseOnLoad = true;
djConfig.baseUrl = "../../../../";
djConfig.scopeMap = [
["dojo", "dojo10"],
["dijit", "dijit10"],
["dojox", "dojox10"]
];
</script>
<script type="text/javascript" src="../../../../dojo.js"></script>


putting this to use
after getting the groundwork laid, now we can do something with this. to use the old version, we just keep doing things like they were being done before. calling dojo.require or using a dojoType tag or calling dojo.byId or dojo.addOnLoad will all use the 0.4.3 version of dojo since the 0.4.3 version took the "dojo" namespace. to use the new version, we call dojo10.require or use dojo10Type tags, etc. this is because we mapped the newer version of dojo to use the "dojo10", "dijit10" and "dojox10" namespaces. refer to the book of dojo for limitations to this feature.

<script type="text/javascript">
dojo.require("dojo.widget.DropdownDatePicker");
dojo10.require("dijit._Calendar");
dojo10.require("dojo.date.locale");
dojo10.require("dojo.parser"); // scan page for widgets

dojo.addOnLoad(function(){
dojo.byId("output043").innerHTML = dojo.version.toString();
});
dojo10.addOnLoad(function(){
dojo.byId("output10").innerHTML = dojo10.version.toString();
});

function myHandler(id,newValue){
console.debug("onChange for id = " + id + ", value: " + newValue);
}

function foobar(){
dojo.byId("typeOut").innerHTML = (typeof dojo.addClass);
}
setTimeout(foobar, 2000);
</script>


html
finally, we get to the html. the most important thing to notice here is the tags - "dojoType" vs "dojo10Type". you will also notice that class="tundra" was put on the parent of the dijit._Calendar so that the tundra theme would be applied to that widget.

<h1>Multiversion Dojo: 0.4.3 and 1.0</h1>

<p><b>NOTE: This test only works with a built version of Dojo</b></p>

<p>This page loads Dojo 0.4.3 and Dojo 1.0.</p>

<p>Dojo 0.4.3 version: <span id="output043"></span></p>

<p>Dojo 1.0 version: <span id="output10"></span></p>

<p><b>dojo.addClass should be undefined:</b> <span id="typeOut"></span></p>

<p>
<input dojoType="dropdowndatepicker" value="2006-10-31"
containerToggle="wipe" containerToggleDuration="300" >
</p>

<p class="tundra">
<input id="calendar1" dojo10Type="dijit._Calendar" onChange="myHandler(this.id,arguments[0])">
</p>

this was a problem i helped someone solve recently and thought it was worth blogging. feel free to leave comments - good or bad.

October 16, 2008

alternative syntax highlighting

an alternative way to do syntax highlighting is using the dojo toolkit. dojo toolkit is a javascript library.

add this code to your template in the same place described in the previous post.


<link href='http://o.aolcdn.com/dojo/1.2/dojox/highlight/resources/highlight.css' rel='stylesheet'/>
<link href='http://o.aolcdn.com/dojo/1.2/dojox/highlight/resources/pygments/default.css' rel='stylesheet'/>
<script djConfig='parseOnLoad: true' src='http://o.aolcdn.com/dojo/1.2/dojo/dojo.xd.js'/>
<script>
dojo.require("dojox.highlight");
dojo.require("dojox.highlight.languages._all");
dojo.require("dojox.highlight.languages.pygments._www");
dojo.addOnLoad(function() {
dojo.query("code").forEach(dojox.highlight.init);
});
</script>

put all your code inside <pre> and <code> blocks and use a <code class="lang"> to define the language for the code block. valid values for lang are:
  • cpp
  • css
  • delphi
  • django
  • html
  • javascript
  • python
  • sql
  • xml
unfortunately, since php is not supported, i probably won't end up using this option. also, feel free to suggest any other options that i might like better than the 2 i've found so far.