Defining objects
Since Javascript doesnt have namespaces, or the class keyword, there
are a number of ways objects can be defined. Often an object in js is just a way to reduce the polution of the namespace with global functions.
The simplest way to define a class is for static use
aaa = {
ccc: "test",
bbb : function() { alert(this.cc); }
}
aaa.bbb();
I'm comming to the conclusion that this is the best way to deal with
code that will only have one instance, eg the main layout, or a widget.
You cannot instantate this as an object (no "new" needed), extending it is possible, (although
I've not tested this idea yet), Most of the User Interface creation and
reaction code is now being written this way.
It has a number of advantages over the next method, for developing, as
accessing the variables for debugging is feasible.
This is really a evolution for my old code as well, as previously I tended to use function name prefixes, and binding that into the object.
Class definitions with private variables
aaa = (function () {
var myprivate;
return {
setIt: function(val) { myprivate = val; }
getIt: function() { return myprivate; }
}
})();
This method is common in extjs for libraries where Jack does not
want users changing or reading the private variables. It however leaves
you with the problem that inspecting those variable for debuging is
difficult. It has a slight benefit that you can avoid putting "this." to access your
private variables.
Extending objects with Ext.extend
function MyCombo (config) {
// set up your datasource here..
MyCombo.superclass.constructor.call(this,config);
}
Ext.extend(MyCombo, Ext.form.ComboBox, {
displayField:'title',
typeAhead: true,
loadingText: 'Searching...',
forceSelection: true,
allowBlank: false,
width: 160,
minChars: 1,
pageSize:10,
hideTrigger:true,
displayField: 'FullName',
valueField: 'id'
}
This resembles the more classic object definition pattern, and is the
core behind extjs. It's handy for writing common reusable components,
and extending extjs objects to create your application specific widgets.
Fixing bugs and miss-behaviour in extjs
Every so often when using extjs, you come across issues with the
libraries that result in widgets not behaving how you want them to (or expect them to.).
To fix this you have a few options
-
redeclare the whole class.
-
extend the class overriding the methods or properties that you need fixed.
-
overide one or more of the objects prototypes
eg. Fix to use the input maxWidth field - so that overtyping results in chopped data.
Ext.form.TextField.prototype.initValue = function()
{
if(this.value !== undefined){
this.setValue(this.value);
}else if(this.el.dom.value.length > 0){
this.setValue(this.el.dom.value);
}
if (!isNaN(this.maxLength) &&
(this.maxLength *1) > 0 &&
(this.maxLength != Number.MAX_VALUE)) {
this.el.dom.maxLength = this.maxLength *1;
}
};
write - test - fix cycle
I normally use write on commit with an svn mounted drive, so after
saving the file in my editor, I can reload the page from a remote server in firefox. The
only snag with this approach is that I'm often only editing a small
part of the application, and reloading the whole lot is both
timeconsuming (well a second or two!) and a little network intensive..
As I have been breaking the classes that deal with specific area's of
the code into seperate .js files, I really only need to reload that js
file. So after a bit of googling and hacking I came up with this simple
funciton. -
include = function(url){
var con = new Ext.data.Connection();
con.request({
url: url + '?ts=' + (new Date().format('Ymd_his')),
method: 'GET',
callback: function(opts, success, response) {
if (!success) {
Ext.MessageBox.alert("Error",
success ? response.responseText :
"Error saving data - try again");
return;
}
var o = document.getElementById('script_' + url)
if (o) {
o.parentNode.removeChild(o);
}
s = document.createElement('script');
s.setAttribute('id', 'script_' + url);
s.setAttribute('type', 'text/javascript');
s.appendChild(document.createTextNode(response.responseText));
document.getElementsByTagName("head")[0].appendChild(s);
}
});
}
I tried various other methods, like adding <script> tags with src in DOM, but the browser refused to refresh the code.. - so this method of dumping the source inside of a script tag seems to work best for testing.
It works pretty much like good ole PHP's include method. eg.
include("MyProject/templates/images/js/mylib.js");
Which can be run at the command line in firebug. Refreshing the object
definition, enabling me to test changes to dialogs and forms without
having to reload the whole page.
I guess hoping that Ctrl-S in the editor could trigger that to happen automatically may be going a bit far...
HTML morphing or just javascript
When I started using extjs, I had tried out dojo, and quite liked the
way it used HTML attributes to create dynamic components. I had got to
the point of writing simple Helper Class to convert tables and forms
into grids and forms with Ext.form elements, and although it worked. I
later attempted to just define my dialog, form and grids directly only using
javascript and extjs.
Having tried this alternative, it's pretty clear that the original idea
of using HTML attributes doesnt really have much advantage, and
espicially combined with the include() method the idea of using pure
javascript to define these components has proved far more efficient. In
reality I dont think I will resort to HTML for much more than text
content areas that need rendering. The whole layout, dialogs, forms
etc will now be purely done in Javascript.
Moving to Extjs's XMLHttpRequest call.
As usual when you start using a library, You never get time to learn
the whole thing, so I had initially been relying on my old
XMLHttpRequest wrapper to send data to the server, but as I began to
dig deeper into extjs, I was able to start removing my old Wrapper
code and use the code in extjs. Which Is better tested (as my library
was originally morphed from some code that worked with the XUL app's I
was writing). And also alot more powerfull.
The save code for my dialogs looks a bit like this..
dialog.el.mask("Saving...");
var con = new Ext.data.Connection();
con.request({
url: baseURL + "/mypage.txt", // where to post to..
params: params, // << key value object with data to send
method: 'POST',
callback: function(opts, success, response) {
dialog.el.unmask(true); // unmask the dialog.
if (!success || ("OK" != response.responseText)) {
Ext.MessageBox.alert("Error", success ?
response.responseText :
"Error saving data - try again");
return;
}
dialog.hide();
// code to update stuff that's affected by the saved form working..
// goes here...
}
});
Submitting forms.
Having moved to using Ext.form.Form, one snag I did find was that the default method for getting form values was
var ar = form.getValues();
unfortunatly, this urlencodes both the key and value of the data, so
using that with the code above to send the data makes a complete
mess..
This little trick helps solve that..
Ext.form.Form.prototype.getValuesRaw = function() {
var ar = this.getValues();
var ret = {};
for(var i in ar) {
ret[decodeURIComponent(i)] = decodeURIComponent(ar[i]);
}
return ret;
};
Comboboxes - load once, not all the time.
The combo box on ExtJs, can autocompelete using JSON calls to the
server, With lists that dont change often, I considered this a bit of
an overkill, so by calling the load method in the extended class, and
setting the mode to 'local', I was able to deliver a more efficient
combo.
function myCombo() {
this.Store = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({
url: baseURL + '/MyLister.js'
}),
reader: new Ext.data.JsonReader({
root: 'data',
totalProperty: 'totalCount',
id: 'id'
}, [ 'id', 'fullname' ])
});
}
this.Store.load();
config.store = this.Store;
myCombo.superclass.constructor.call(this, config);
}
Ext.extend(FlexyCash.AccountCombo, Ext.form.ComboBox, {
mode: 'local',
.....
});
Then if you need to refresh the list at any time (eg. before the dialog appears), just call the combo.store.load() method..
IE Gotcha's
Normally when I do any web development, It seems like 20% at the end of the project is spent working around IE bugs, so even with my extjs projects I've been developing in firefox and testing later with IE. However the only serious issue that I've found that catches me out has been that IE doesnt like trailing comma's in object definitions.
var a = {
a: "b",
c: "d", //<< oops trailing comma!
};
Otherwise It's pretty easy going developing cross platform applications with extjs..