Demystifying dojo.data

I have a love/hate relationship with dojo.data.

I absolutely love the way that it simplifies my life. While it’s not quite as “magic” as dojo.hitch(), things “just work” when I use it. Many of the dijit widgets support being tied to data stores. The drop-in replaceability of the stores is another great benefit. When working on large projects, it is very helpful to have something to work with on the UI side while the server-side is still being developed. One solution is to do this:

var dummyData = {
	identity: "key",
	label: "label",
	data: [
		{ key: "firstItem", label: "My First Item", ... },
		...
		{ key: "lastItem", label: "My Last Item", ... }
	]
};
var myDataStore = new dojo.data.ItemFileWriteStore({data:dummyData});
 
// Program away using dataStore...

Once the server guys get their stuff together, you whip up your own custom datastore (hopefully, you can leverage one of the many already-existing stores) and change your code to read:

var myDataStore = new my.custom.DataStore({url:"serverUrl"});
 
// Program away using dataStore...

And everything just works (hopefully)! If it doesn’t, it was probably the server guy’s fault. Life is wonderful.

However, I glossed over the hate part of my relationship. The part above that reads:

// Program away using dataStore...

can be a bit difficult. I find myself asking “why” every time I start to work with a datastore, and I hope to clarify some of those issues.

Why store.getValue()?

Probably the first thing that bites most people is accessing the values in your items. Why can’t I get values using:

// The "javascript" way of doing it
value = item.attribute;
// Another way
value = item["attribute"];
// This really *should* work (but it doesn't)
value = item.getValue("attribute");

All of these methods are item-centric. It’s the way we’ve been programming in Javascript since we were kids. However, it distributes the logic of your data among each of the items. By switching to a store-centric way of accessing our data, we can potentially gain a lot of efficiency:

value = store.getValue(item, "attribute");

In doing this, the store is “in charge” of the data - it’s the “smart” one. It allows for lazy loading of attributes, and an additional level of abstraction. Imagine a store with massive amounts of data that the store maintains in a compressed form until you ask it for a specific value. Sure, most store implementations will probably just wrap getValue() around item.attribute, but accessing via the store isn’t much of a price to ask when you can gain some pretty amazing flexibility and performance.

Why store.fetch()

The next wall you will likely hit is the asynchronous nature of store.fetch() - and his brother, store.fetchItemByIdentity(). Why is it necessary for these functions to be asynchronous? Why can’t I just do:

// This doesn't work!
items = store.fetch({query: {attribute: true}});
for(i = 0; i < items.length; i++){
	var myItem = items[i];
	// do something with myItem
}

The main reason, again, is flexibility. Not all stores will have their data available at the beginning. In fact, not all stores will truly have their data when they give you an item. It’s much easier to have you work asynchronously than it is to force those backends to perform synchronously. Here is a thought process you can go through to make your code more “async”-friendly. First, rewrite using dojo’s uber-cool forEach():

// Still doesn't work!
var itemFx = function(myItem, i){
	// do something with myItem
};
items = store.fetch({query: {attribute: true}});
dojo.forEach(items, itemFx);

Now it’s easy to make it asynchronous:

// Now it works!
var itemFx = function(myItem, i){
	// do something with myItem
};
items = store.fetch({query: {attribute: true}, onItem: itemFx});

The above case is a very common case. Usually, if you want to get the items from your store, you are probably wanting to do something with those items. One of the more common questions that I’ve seen is “How can I make this call synchronous?” If you find yourself asking that, you should reexamine what you are trying to accomplish, and change your approach to be more asynchronous. You’ll gain a lot of performance benefits in the long run. Remember, dojo is an Ajax library - not an Sjax library.

Hopefully these answers will keep you from pulling out your hair - I wish I would have known about them earlier…

Tags: