PDA

View Full Version : How to refer to a lazy instantiated element from a handler function



jaime_
23 Dec 2009, 5:55 AM
Hi everybody,

After a while overlooking code, examples and this forum, I'm starting to get serious about learning Ext. Yesterday I ordered a couple of books, but I'm afraid I do and I will still need some help.

I've started an app to teach myself. I've defined a preconfigured class for a login screen. It's just a window with a form inside. I want to take advantage of lazy instantiation so I used xtypes. I'm extending Ext.Window like this:



G.window.login = Ext.extend(Ext.Window, {
title: 'Login',
constructor: function(config) {
G.window.login.superclass.constructor.call(this, Ext.apply({
xtype: 'form',
id: 'loginform',
itemId: 'loginform',
url: '/users/login',
labelWidth: '30%',
frame: true,
monitorValid: true,
layout: 'form',
defaultType: 'textfield',
defaults: { width: '80%' },
items: [ /* A couple fields. Removed for clarity*/],
buttons: [{
text: 'Enter',
formBind: true,
handler: function() {
Ext.getCmp('loginform').submit({ /* Removed for clarity */ });
}
}]
}, config));
},
});
After two days messing around, I cannot figure out the following:



The submit() handler is not working:

Ext.getCmp("loginform").submit is not a functionI tried to debug it with Firebug:

console.info(Ext.getCmp('loginform'))Getting:

Object initialConfig=Object xtype=form id=loginformThe question is: how can I refer from my constructor to a form I've configured as an xtype, and which is therefore not yet instantiated? I've read this question on other threads, but no answer worked for me. Notice I even set both itemId and id, with no luck.
I know there is no point on extending a class for a login screen, which by definition won't be instantiated more than once, but I'm using it as an example to teach myself.

The question is: am I doing it right? Is absolutely necesarry to override the constructor? I've found it's the only way to preconfigure the items property in a window. If, as I guess, this is the only way, is this the best way to do it?

Thank you for any possible hint and hope this helps any other in the future.

23 Dec 2009, 6:08 AM
or, set the button to the scope of "this" and the handler to "this.submit"

23 Dec 2009, 6:10 AM
Btw, the following example will debute in CH16 of my book (almost complete), and demonstrates a nice and readable pattern to extending a Window.


Ext.ns("TKE.window");

/**
* @class TKE.window.EmployeeAssociationWindow
* @extends Ext.Window
* An Ext.Window implementation that contains at least two implementations of {@link TKE.chartpanel.ChartPanelBaseClass}. <br />
* The first (left) chart is a
* It uses the {@link Ext.layout.VBoxLayout vbox} layout to organize the {@link TKE.chartpanel.ChartPanelBaseClass} implementations.
* <br />
* @constructor
* @param {Object} config The config object
* @xtype company_snapshot_panel
**/

TKE.window.EmployeeAssociationWindow = Ext.extend(Ext.Window, {
width : 600,
height : 400,
maxWidth : 600,
maxHeight : 500,
modal : true,
border : false,
closable : false,
center : true,
constrain : true,
resizable : true,
departmentName : ' ',
departmentId : null,
layout : {
type : 'hbox',
align : 'stretch'
},

initComponent : function() {

Ext.apply(this, {
title : 'Add employees to ' + this.departmentName,
buttons : this.buildButtons(),
items : [
this.buildListViewPanel(),
this.buildGridPanel()
]
});

TKE.window.EmployeeAssociationWindow.superclass.initComponent.call(this);
this.addEvents({
assocemployees : true
});
},

buildButtons : function() {
return [
{
text : 'Close',
iconCls : 'icon-cross',
scope : this,
handler : this.onClose
},
{
text : 'Add',
iconCls : 'icon-user_add',
scope : this,
handler : this.onAddToDepartment
}
];
},

buildListViewPanel : function() {
return {
xtype : 'departmentlist',
itemId : 'departmentList',
title : 'Departments',
frame : true,
width : 150,
autoLoadStore : true,
listeners : {
scope : this,
click : this.onDepartmentListClick,
load : this.onDepartmentStoreLoad
}
};
},

buildGridPanel : function() {
return {
xtype : 'employeegridpanel',
itemId : 'employeeGrid',
loadMask : true,
frame : true,
title : 'Employees',
flex : 1
};
},

onClose : function() {
this.close();
},

onAddToDepartment : function() {
var employeeGrid = this.getComponent('employeeGrid');
var selectedRecords = employeeGrid.getSelected();

if (selectedRecords.length > 0) {
this.fireEvent('assocemployees', selectedRecords, employeeGrid, this);
}
},


onDepartmentListClick : function(listView) {
var record = listView.getSelectedRecords()[0];
var employeeGrid = this.getComponent('employeeGrid');
employeeGrid.load({
params : {
id : record.get('id')
}
});

employeeGrid.setTitle('Employees for department ' + record.get('name'));
},

onDepartmentStoreLoad : function(store) {
var deptRecInd = store.find('id', this.departmentId);
store.remove(store.getAt(deptRecInd));
}
});

mschwartz
23 Dec 2009, 7:21 AM
I'm going to steal me a copy of that book!

23 Dec 2009, 7:36 AM
it should be easy ;). There are lots of places where it's pirated already :P

Mike Robinson
23 Dec 2009, 8:43 AM
Welcome to the Internet. 8-|

Fortunately, you still can't take a computer into the bathroom with you for some nice educational reading. ~o)

Everything gets pirated. (Really, people go to extraordinary lengths ... scanning hundreds of pages ...) But it doesn't seem to affect sales that much. Or maybe I'm just cloo-less an' dreamin'. :s

23 Dec 2009, 9:37 AM
Heh. I remember pirating on sneakernet.

mschwartz
23 Dec 2009, 9:52 AM
it should be easy ;). There are lots of places where it's pirated already :P

But I'm going to buy everyone else's book that you've been shamelessly plugging.
B)

jaime_
23 Dec 2009, 10:14 AM
or, set the button to the scope of "this" and the handler to "this.submit"

I'm afraid setting the scope doesn't work either:


buttons: [{
text: 'Entrar',
formBind: true,
scope: this,
handler: function() {
this.submit({...})
...
It does nothing (not even throw an error) when clicking the "Submit" button.

Thank you too for your nice template on how to extend Window, I'll start working on it.


it should be easy ;). There are lots of places where it's pirated already :PAs I said, I bought yesterday a couple of books on ExtJS. I only bought them because I found both on Rapidshare and could download them and confirm that they covered the topics I'm interested in. Then, I was more than happy to pay ~100 USD for them printed. Not to say that I felt good about supporting the authors of such a technical and specialized content.

So this has been a case of piracy resulting in increasing sales, and I don't think it's the only one. Anyway, a "read it online" feature as it exists in Amazon, or in some sites (like http://gettingreal.37signals.com) would have made the same effect to me. It's a long and complex debate, however.

23 Dec 2009, 10:22 AM
Part of the problem is that you're extending a window and expecting the button to bind to a form, etc.

G.window.login = Ext.extend(Ext.Window, {
title: 'Login',
constructor: function(config) {
G.window.login.superclass.constructor.call(this, Ext.apply({
xtype: 'form',
id: 'loginform',
itemId: 'loginform', // Don't set item id here! it should be set by a parent that is aware of it.
url: '/users/login',
labelWidth: '30%',
frame: true,
monitorValid: true,
layout: 'fit',
defaultType: 'textfield',
defaults: {
width: '80%'
},
items: [
Form should be here
{xtype: 'formpanel', itemId:'myForm'} // Form elements should be a child of this form panel
],
buttons: [{
text: 'Enter',
formBind: true, // Not needed, the buttons live outside the form!
scope : this,
handler: this.submitForm

}]
},
config));
},
submitForm : function() {
this.getComponent('myForm').getForm().submit({/* configs here */})
}
});

cerad
23 Dec 2009, 10:30 AM
Bit off topic but I had a question on initialConfig

In your initComponent you basically do: Ext.apply(this,config);

In other examples I have seen:
Ext.apply(this, Ext.apply(this.initialConfig, config));

1. Have you run across cases where it's important to set initialConfig? Does the framework actually use it?

2. initialConfig is marked as a public readOnly variable. Is it okay to treat it as a protected read/write variable? In general, is there a way to know which variables can be considered as protected (i.e. okay to use in an extended class but not publicly) without diving in to the source code?

Thanks

jaime_
23 Dec 2009, 11:19 AM
Part of the problem is that you're extending a window and expecting the button to bind to a form, etc.

Sorry, but it still doesn't work. I'm trying your code but the xtype: 'formpanel' line triggers an error (types[config.xtype || defaultType] is not a constructor).


Ext.ns('G.window');
G.window.login = Ext.extend(Ext.Window, {
title: 'Login',
constructor: function(config) {
G.window.login.superclass.constructor.call(this, Ext.apply({
xtype: 'form',
id: 'loginform',
url: '/users/login',
labelWidth: '30%',
frame: true,
monitorValid: true,
layout: 'fit',
defaultType: 'textfield',
defaults: {
width: '80%'
},
items: [{
xtype: 'formpanel', /* This line triggers an error */
itemId:'myForm'
}],
buttons: [{
text: 'Enter',
scope : this,
handler: this.submitForm

}]
},
config));
},
submitForm : function() {
this.getComponent('myForm').getForm().submit({/* configs here */})
}
});

jaime_
23 Dec 2009, 2:24 PM
Finally I got it. Thank you very much for your help. I was putting the form items inside the window definition, and that was part of the problem, as you pointed out.

Now I can call this.getComponent('myForm').getForm().submit() from my button custom handler. I still don't like very much the idea of having to define a itemId in order to be able to access my form from within my custom handler, but it works OK.

I post the final snippet, for the record, as perhaps it may help any others running the same issue as I did:



Ext.ns('G.window');
G.window.login = Ext.extend(Ext.Window, {
title: 'Login',
initComponent: function() {
Ext.apply(this, {
layout: 'form',
items: [{
xtype: 'form',
itemId: 'myForm', /* Important */
url: '/users/login',
defaults: {
xtype: 'textfield',
},
items: [{
fieldLabel: 'Usuario',
name: 'data[User][login]',
}, {
fieldLabel: 'Contraseņa',
name: 'data[User][password]',
inputType: 'password',
}],
buttons : [{
text: 'Entrar',
scope: this,
handler: this.mySubmit
}],
}],
});
G.window.login.superclass.initComponent.call(this);
},

mySubmit: function() {
var form = this.getComponent('myForm').getForm(); /* This is the trick */
form.submit({});
}
});

23 Dec 2009, 2:42 PM
There is more than one way to do what you want. ItemId is just an example I provide for newbies. You can use this.items.items[0]