Saturday, November 20, 2010

XJS: Avoiding of Anonymous Functions Example

One of the difficulties one faces quickly using the ExtJS framework is the code organization. Any project quickly grows into a jungle of anonymous functions and heavily nested configuration objects. Hard to read and hard to maintain.
Examples commonly start simple but as soon as more elements are added they turn off newbies. A configuration object spanning multiple screens is not easy to digest, but this is how most online examples are presented. This, according to my informal survey, discourages new developers from proceeding.
This, in my opinion, is one of the greater roadblocks to learning the framework and making scalable ExtJS apps. I understand that changes in ExtJS 4 will introduce a more mature MVC based application framework; however, the examples I have seen are still heavily reliant on multi-level nesting and anonymous functions.
It is of course, a matter of preference, but I do find organizing code written in that fashion harder to read and manage long term.
For once, common mistakes, such as missing a semicolon, comma, or bracket closure, result in disproportionate debugging time. On the other hand, having multiple team members working in their own "sandbox" is hard to do if everyone is trying to change the same file.
Thus, I was looking for alternate organization but could not find a simple example to show that another approach was possible.

Take this common ExtJS code as example (this is a fairly simple example in ExtJS realm). It draws a border layout based viewport:


Ext.onReady(function(){
new Ext.Viewport({

layout: 'border',
items: [{
region: 'north',

html: '<h1 class="x-panel-header">Page Title</h1>',
autoHeight: true,

border: false,
margins: '0 0 5 0'
}, {

region: 'west',
collapsible: true,
title: 'Navigation',

width: 200
// the west region might typically utilize a TreePanel or a Panel with Accordion layout
}, {
region: 'south',

title: 'Title for South Panel',
collapsible: true,
html: 'Information goes here',

id: 'southPanel',
split: true,
height: 100,

minHeight: 100
}, {
region: 'east',

title: 'Title for the Grid Panel',
collapsible: true,
split: true,

width: 200,
xtype: 'box'
// more nested code could be added here, e.g a GridPanel

}, {
region: 'center',
xtype: 'tabpanel', // TabPanel itself has no title

items: {
title: 'Default Tab',
html: 'Tab conent'
}
}]
});
});'


The formatting is nicely done, but still requires some getting used to. Only a few nested elements in this one yet the complexity can be already seen. What if you had a team of developers each working on one of the panels?

I prefer a more verbose but easier maintainable format that achieves the same result while allowing easier developer task separation (the use of multiple script tag is optional, I wanted to show a sense of separation of code):



<script type="text/javascript">
//we define a namespace to use for organization. This is optional.
Ext.namespace('startup');
</script>

<!-- panel definitions could be in another file -->
<script type="text/javascript">
// each panel could be seperate script file maintained by seperate developer or dynamically included via

// app server such as PHP / Railo / etc.
startup.northpanel = {
region: 'north',

html: '<h1 class="x-panel-header">Page Title</h1>',
autoHeight: true,

border: false,
margins: '0 0 5 0'
};


startup.westpanel = {
region: 'west',
collapsible: true,

title: 'Navigation',
width: 200
};

startup.southpanel = {

region: 'south',
title: 'Title for South Panel',
collapsible: true,

html: 'Information goes here',
id: 'southPanel',
split: true,

height: 100,
minHeight: 100
};


startup.centerpanel = {
region: 'center',
xtype: 'tabpanel', // TabPanel itself has no title

items: {
title: 'Default Tab',
html: 'Tab Conent.'

}
};

//assemble panels here into array, alternatly this array could be the items definition
startup.aItems = [startup.northpanel,startup.westpanel,startup.southpanel,startup.centerpanel];

</script>

<script type="text/javascript">
startup.DoInit = function () {

//create view port
var bport = new Ext.Viewport({
layout: 'border',

items: startup.aItems
});
}
</script>


<script type="text/javascript">
//the actual ExtJS onReady function remains very small
Ext.onReady(function(){
startup.DoInit();

}); //end onReady
</script>


Of course, there is the element of preference and argument about throw away functions, e.g. only run once; I would still prefer good naming and definition as this is the long run makes my life easier.

Feel free to comment.

Cheers,
-B

No comments: