In our project we used to work with Ext.getCmp
method if we need some dynamic interactions
with our components. Which is not a very good experience actually. Instead of calling
Ext.getCmp('usersPanel').doLayout()
I would prefer just use this.usersPanel.doLayout()
.
To simplify our life we just can assign all needed fields in initComponent
method:
initComponent: function() {
Ext.apply(this, {
items: [{
xtype: 'panel',
id: 'usersPanel'
}]
});
this.callParent(arguments);
this.usersPanel = Ext.getCmp('usersPanel');
}
Could be a nice solution, but there are some drawbacks: id
property will correspond
to html id
attribute which should be unique in scope of html page. If we need to
create several instances of our custom control we will run into troubles.
Ok, there is another way to achieve our aim:
initComponent: function() {
this.usersPanel = new Ext.panel.Panel();
Ext.apply(this, {
items: [this.usersPanel]
});
this.callParent(arguments);
}
Well, quite straightforward way for autogenerated content, but will become quite
unreadable in a component with several components. I can not call Ext designer very
smart tool as well when it gets to the part with modifying generated components.
Happily enough there is a built in support for local ids in Ext JS.
initComponent: function() {
Ext.apply(this, {
items: [{
xtype: 'panel',
itemId: 'usersPanel'
}]
});
this.callParent(arguments);
this.usersPanel = this.getComponent('usersPanel');
}
It has a pitfall though. Method getComponent
works only with items collection, but
for deep hierarchy this approach doesn't work. My components usually have several
layers of nested components. Especially when some layout work is required.
After some thinking we decided to allow components register themselves in a root
container when they are created. Performing this work in initComponent
is a good
idea, but we need to have a reference to a container itself. So, initialConfig
must
contain two additional arguments: local identifier and parent component. We decided
to do one pace further and make it more concise to be written in one line. Something
like this: saveAs: { component: this, fieldName: 'usersPanel' }
Let's stop here for a while and discuss one small tip for JavaScript. Managing scope
for code should be done carefully. If not it is easy to get some annoying 'undefined'
errors. To simplify life we can just store a reference to an object in a variable
once and let closures work for you. Usually I define me
variable in the beginning
of instance method which is equal to this.
initComponent: function() {
var me = this;
// ...
}
Back to our instance fields stuff let just make it more fluent: save: me.as('usersPanel')
As you may guess we defined a method as in current component which just returns
a new object similar to { component: this, fieldName: 'usersPanel' }
.
All components in Ext JS are descendants of Ext.Component
, it is a good place to
put our registration extensions there:
Ext.Component.implement({
as: function (fieldName) {
return {
component: this,
fieldName: fieldName
};
}
});
Ext.Component.override({
initComponent: function() {
this.callOverridden();
if (this.initialConfig.save) {
this.save.component[this.save.fieldName] = this;
}
}
});
Our initial example will look like:
initComponent: function() {
var me = this;
Ext.apply(this, {
items: [{
xtype: 'panel',
save: me.as('usersPanel')
}]
});
this.callParent(arguments);
// usersPanel is created and registered
// me.usersPanel is available
}
You can check working example of this technique.