In the first of this series of couple of blog posts (available here), I have discussed the problem of maintaining reference to ExtJs components on the client-side in javascript, and the motivation for creating a new solution to it, than what ExtJs/Ext.Net provide out-of-the box. Well, here’s how I have settled down on resolving the issue.

In a nut-shell, I use a dedicated namespace for maintaining Component references for all Components that are logically associated and which need to be accessible in each other’s event listeners. Now, follow the implementation details.

It all starts with creating a new namespace for maintaining references for all logically associated components. Here’s how:

 

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }Ext.ns(‘Rahul.Ext’);

Rahul.Ext.allocateControlNamespace = function(parentNamespace) {
var fn = function() {
if (Rahul.Ext.allocateControlNamespace.counter == undefined) {
Rahul.Ext.allocateControlNamespace.counter = 0;
}
return (Rahul.Ext.allocateControlNamespace.counter++);
}

var ns, fqn;
if (Ext.isEmpty(parentNamespace)) {
parentNamespace = Ext.ns(‘Rahul.Ext.Controls’);
ns = Rahul.Ext.Controls;
fqn = ‘Rahul.Ext.Controls’;
} else if (typeof (parentNamespace) == ‘string’) {
if (!parentNamespace.endsWith(‘.Controls’)) {
parentNamespace = parentNamespace + ‘.Controls’;
}
fqn = parentNamespace;
Ext.ns(parentNamespace);
ns = eval(parentNamespace);
} else {
throw ‘parentNamespace should be specified as a string.’;
}

var i = fn();
ns[‘ns’ + i] = {};
fqn += ‘.ns’ + i;

ns = ns[‘ns’ + i];
ns.reg = function(name, ctl) {
//Please take note that reg can either be called automatically in iniComponent, or you can call it explicitly, like ns.reg(‘ctl’, yourControl);
//Therefore, you need to register references both ways.
ctl.ns = ns;
ns[name] = ctl;

return (ns);
}
ns.getFullName = function() {
return (fqn);
}

return (ns);
}

String.prototype.endsWith = function(str) {
var lastIndex = this.lastIndexOf(str);
return (lastIndex != -1) && (lastIndex + str.length == this.length);
}{/syntaxhighlighter}

The parentNamespace argument to the method is optional above, and if specified, should be a string. The method generates a unique namespace for storing Component references and returns the namespace generated. The namespace has 2 methods, reg which should be used to register a Component reference with this namespace, and getFullName, which returns the fully qualified name of the namespace as a string (something on the lines of Rahul.Ext.Controls.ns0 if parentNamespace is not specified). endsWith is a utility method added to the String class.

Next at the beginning of instantizing the controls, which are logically related, I get allocated a new namespace as follows:

 

 

var ns = Rahul.Ext.allocateControlNamespace();

 

Then, in the config options for any ExtJs components, I specify 2 custom attributes, i) ns which corresponds to the namespace we just got allocated, and ii), nsName, the name with which the component should be registered with the namespace. For example:

 

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }var ns=Rahul.Ext.allocateControlNamespace();
var pn = new Ext.Panel({
title: ‘Login’,
layout: ‘form’,
listeners: { show: pnlLoginShown },
ns: ns,
nsName: ‘pnlLogin’,
items: [{ xtype: ‘textfield’, fieldLabel: ‘Username’, ns: ns, nsName: ‘txtUsername’, listeners: {specialkey:txtLoginSpecialKey} },
{ xtype: ‘textfield’, fieldLabel: ‘password’, ns: ns, nsName: ‘txtPassword’, listeners: { specialkey: txtLoginSpecialKey} }
]
});{/syntaxhighlighter}

Now, we need to hook into ExtJs’ component instantization process, so that the components get registered with the namespace when they are instantized. To do this, we create a sequence method for Ext.Component.initComponent method:

 

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }Ext.Component.prototype.initComponent = Ext.Component.prototype.initComponent.createSequence(function() {
if (this.ns && this.nsName) {
if (Ext.isFunction(this.ns.reg)) {
this.ns.reg(this.nsName, this);
} else {
this.ns[this.nsName] = this;
}
}
});{/syntaxhighlighter}

Well, that’s it, we have done majority of the work needed. Now you can easily reference any component in an event listener for any other components, provided they got registered to the same namespace while instantization. e.g.

 

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }function pnlLoginShown(el) {
var ns = el.ns;

var username = ns.txtUsername.getValue();
var password = ns.txtPassword.getValue();
//Your processing here
}

function txtLoginSpecialKey(el) {
var ns = el.ns;

var pnlLogin = ns.pnlLogin;
var username = ns.txtUsername.getValue();
var password = ns.txtPassword.getValue();
//Your processing here
}{/syntaxhighlighter}

As you can see, accessing other components is as easy as referencing them from the associated namespace, no need to traverse along parent/child container axis, or needing to manually maintain unique ids. You just need to pass a couple of extra config options for Components, which need to be referenced together.

Please note Ext.Component.initComponent needs to be sequenced only once (and not each time you get allocated a new control namespace).

That was for maintaining references while creating components in javascript. What about if you are using Ext.Net/Coolite. How do you maintain references then. Well, you again have 2 easy ways to do so. But’ let’s first see a small sample of Ext.Net markup:

 

 

<ext:Panel runat="server" ID="pnlLogin" Title="Login" Layout="Form">
	<Items>
		<ext:TextField runat="server" ID="txtUsername" />
		<ext:TextField runat="server" ID="txtPassword" />
	</Items>
</ext:Panel>

 

There are 2 ways you can maintain references for your Ext.Net controls inside the same page, or same UserControl. The first one uses CustomConfig available on each control, that I do not necessarily recommend. My other approach is to add an extra <ext:Hidden> control to the Page or the UserControl, and manually register references in its Render handler. Again an example is in order:

 

{syntaxhighlighter brush: xml;fontsize: 100; first-line: 1; }<ext:Hidden runat=”server”>
<Listeners>
<Render Handler=”
var ns = MB.allocateControlNamespace(null, true);

ns.reg(‘pnlLogin’, #{pnlLogin});
ns.reg(‘txtUsername’, #{txtUsername});
ns.reg(‘txtPassword’, #{txtPassword});
” />
</Listeners>
</ext:Hidden>{/syntaxhighlighter}

Please take care that this Hidden Control is always towards the bottom of the Page or UserControl. There’s no change on how you reference components in the javascript event listeners for your Ext.Net/Coolite components, and that remains the same as demonstrated above.

Is is interesting to take note that you can indeed use any javascript object while specifying the ns custom config option (and not necessarily the retirn value of the allocateControlNamepsace method). If the ns passed has a reg method, the initComponent method registers the control through the reg method, otherwise it assigns the reference directly.

The above approach allows you to maintain component references pretty easily and access all registered components easily in any event listener, or else wise, whenever you have reference to any single component which was registered with others to a common namespace.

All your ASP.NET UserControls and Pages remain independent and you can choose any Control Id independently from what has been chosen on other Pages or UserControls for the components. Just ensure that you have not specified one of the Explicit options for your Ext.Net controls’ IDMode property as that forces you to manually ensure unique ids across UserControls.

For communication between UserControls, I have well-defined server and client-side interfaces, that allow Components in one UserControl to access Components in other UserControls. However, as these interfaces are highly specific to the particular application they have been developed for, they are not suitable for discussion here.