PDA

View Full Version : Dynamic Form Creation Scope problem



duskandawn
17 Feb 2011, 11:34 AM
Hello All,
I am trying to create a dynamic formpanel, which will get populated on the basis of some server side gui object which will contain the name of the fieldsets which should be added to the formpanel at runtime.
So, i have create a FieldSetConfig.js containing all fieldset config which looks like :



Ext.ns("Test.FieldSets");


Test.FieldSets.TestMe = {
xtype: 'fieldset',
title : 'Information',
defaults : { anchor:'50%' },
items: [{
xtype:'textfield',
fieldLabel: 'CAS',
}, {
xtype: 'button',
text: 'Save',
anchor: "15%",
scope: this,
handler: this.test
}],

test : function(){
alert("called me ");
}
};


And in my FormPanel which needs to get updated with the above fieldset looks like :



Ext.ns("Test");
Test.DynaForm = Ext.extend(Ext.FormPanel, {
id: 'DynaForm',
title : 'Dynamic Form',

initComponent : function() {
Test.DynaForm.superclass.initComponent.call(this);
for(var i in this.fieldSetConfig){
this.add(this.fieldSetConfig[i]);
}
this.doLayout();

}

});



And my final JSP which contains which contains Ext.onReady() looks like :



Ext.onReady(function(){

var configDummy = {
fileld1 : 'Test.FieldSets.TestMe' // defines the dummy config which will be sent from server
};
var tempArr = [];
for(i in configDummy){
console.log(i + '=' + configDummy[i]);
tempArr.push(Ext.decode(configDummy[i]));

}

Ext.QuickTips.init();

var myTest = new Test.DynaForm({
fieldSetConfig : tempArr
});



With the above code inplace, when i access my JSP i see the fieldset being rendered correctly inside the formpanel.
But when i try to click the save button, nothing happens. I know its something related to the scope in which it is being run, but cant figure out.
I tried place test() at other places, but still cannot get it to work.
i can do handler : function() case, but eventually I want all the buttons to use the same handler function.

Can you please help me, where i am going wrong on this, so to how to get hold of button handler ?

And also since this is my first try of creating a dynamic form, if this an acceptable way to do it this way ?

carl23934
17 Feb 2011, 2:26 PM
Your indentation is all jacked up. Before posting, run your code through http://jsbeautifier.org/. It makes it much easier to troubleshoot.

Yes, you definitely have scoping issues. You have to think of where the "this" would be pointing to when the component is created. In your example I think "this" points to the scope inside of your Ext.onReady function.

This would work:



Ext.ns("Test.FieldSets");

Test.FieldSets.TestMe = {
xtype: 'fieldset',
title: 'Information',
defaults: {
anchor: '50%'
},
items: [
{
xtype: 'textfield',
fieldLabel: 'CAS',
},{
xtype: 'button',
text: 'Save',
anchor: "15%",
handler: function (button, event) {
this.ownerCt.test(button, event);
}
}]
,test: function(button, event) {
alert("called me! button text: "+button.text);
}
};
No changes here:


Ext.ns("Test");
Test.DynaForm = Ext.extend(Ext.FormPanel, {
id: 'DynaForm',
title: 'Dynamic Form',

initComponent: function () {
Test.DynaForm.superclass.initComponent.call(this);
for (var i in this.fieldSetConfig) {
this.add(this.fieldSetConfig[i]);
}
this.doLayout();

}

});
No changes here either:
Just added renderTo:document.body to make this a working example

Ext.onReady(function () {

var configDummy = {
fileld1: 'Test.FieldSets.TestMe' // defines the dummy config which will be sent from server
};
var tempArr = [];
for (i in configDummy) {
console.log(i + '=' + configDummy[i]);
tempArr.push(Ext.decode(configDummy[i]));

}

Ext.QuickTips.init();

var myTest = new Test.DynaForm({
fieldSetConfig: tempArr,
renderTo: document.body
});
});

duskandawn
17 Feb 2011, 2:42 PM
Hi carl23934,
Yes the code you pasted did work.
But what i want is a global handler function, which should work when i add some new buttons also. Right now its inside the fieldset object only.

Also, i wanted to know how does wrapping the this.test() inside function(){} mitigate the problem.
I am still a newbie trying to understand the working.
Can you please elaborate.
Thanks,

carl23934
17 Feb 2011, 2:48 PM
Button handlers are called within the scope of the button (unless otherwise specified with a scope param). "this" means "this button". The button handler needs to get to the function that is 1 component up. That is what this.ownerCt does.

So if you want to move the handler to DynaForm, you can use this.ownerCt.ownerCt.test();

This is just one of the many issues that you will run into with dynamically generated javascript. It requires careful planning and a good understanding of scoping to do it right. That is why I don't use dynamic code! ;)

darthwes
17 Feb 2011, 4:14 PM
dynamic forms ftw. Your 'this' reference is gonna be global/domwindow if you use it there. To use the scope like you wanted, just put it in a literal function like function () { this.whatever() } so that this will be scoped to the button (or the button's scope if you're defining it).

Yeah sometimes ownerCt chains are helpful, though I would point you to the 'ref' property, which is layer of abstraction above the ownerCt thing. From what I can recall, ownerCt is set after rendering to the dom, so watch out for that. 'ref' lets you do some nifty stuff to avoid the ugliness of walking the ownerCt chain yourself.

duskandawn
17 Feb 2011, 4:55 PM
Hi,
Thanks a lot for your advices. I tired the first approach of ownerCt.
According to the doc it says :
"This Component's owner Container (http://dev.sencha.com/deploy/dev/docs/output/Ext.Container.html)"

So if i am using this.ownerCt.ownerCt.test();
i am confused, since :
this.ownerCt -- > will be the DynaForm since it the container of my button.
Why do i need add one more level up on chain ?

duskandawn
17 Feb 2011, 5:12 PM
@carl23934,
Sorry for the trouble i think i got it.
Why we needed the two ownerCt's, one for my fieldset and the other for my dynaform.

The reason i have to do the dynamic form is , all the filedset will be dynamic based on some input, and the same tabs will be looking different for different users.

DO you have other ideas of how to tackle suck kind of problem.

@ darthwes
I am trying the ref option which you suggested, although i am not clear how exactly works.
Will keep you posted.

Once again thank you guys for all your help.
Much appreciated.

darthwes
17 Feb 2011, 6:28 PM
var stTest = new Ext.data.ArrayStore({
//autoload: true,
fields: ['jStat',
{
name: 'stVal',
type: 'float'
}, {
name: 'mVal',
type: 'float'
}],
data: [
['One', 3.79, 1.84],
['Two', 7.58, 3.24],
['Three', 0, 0.06],
['Four', 2.27, 1.75],
['Five', 0, 0],
['Six', 0, 0],
['Seven', 0.76, 0.21],
['Eigth', 0.76, 1.16]
]
});

var chTest = new Ext.grid.EditorGridPanel({
store: stTest,
cm: new Ext.grid.ColumnModel({
columns: [{
dataIndex: 'jStat',
header: "Competitor"
}, {
dataIndex: 'stVal',
header: "Price"
}, {
dataIndex: 'mVal',
header: "Quality"
}]
}),
title: 'Jazzy',
tbar: [{
xtype: 'button',
text: 'wes',
ref: '../wesButton',
handler: function () {
console.log('heres this buttons refOwner (the grid)', this.refOwner);
}
}],
listeners: {
render: function () {
console.log('The grid just rendered. Heres my button', this.wesButton);
}
}
});

I think that shows a pretty good example. Get it?

duskandawn
18 Feb 2011, 8:04 AM
Thanks a lot, did get it !!

carl23934
18 Feb 2011, 8:31 AM
Observe the ownetCt.ownerCt thing:

The good thing about this (even though there is a 2 level ownerCt chain) is that the buttons can be completely decoupled. When you use ref you have additional coupling that makes the design a bit less flexible.



<html>
<body>
<link rel="stylesheet" type="text/css" href="/inc/js/ext-3.3.1/resources/css/ext-all.css" />
<script type="text/javascript" src="/inc/js/ext-3.3.1/adapter/ext/ext-base-debug.js"></script>
<script type="text/javascript" src="/inc/js/ext-3.3.1/ext-all-debug.js"></script>

<script type="text/javascript">
Ext.ns("Test.FieldSets");

Test.FieldSets.TestMe = {
xtype: 'fieldset',
title: 'Information',
defaults: {
anchor: '50%'
},
items: [{
xtype: 'textfield',
fieldLabel: 'CAS',
},
{
xtype: 'button',
text: 'Save',
anchor: "15%",
handler: function (button, event) {
this.ownerCt.ownerCt.test(button, event);
}
}]

};
</script>
<script type="text/javascript">
Ext.ns("Test");
Test.DynaForm = Ext.extend(Ext.FormPanel, {
id: 'DynaForm',
title: 'Dynamic Form',

initComponent: function () {
Test.DynaForm.superclass.initComponent.call(this);
for (var i in this.fieldSetConfig) {
this.add(this.fieldSetConfig[i]);
}
this.doLayout();

},
test: function (button, event) {
alert("called me! button text: " + button.text);
}

});
</script>
<script type="text/javascript">
Ext.onReady(function () {

var configDummy = {
fileld1: 'Test.FieldSets.TestMe' // defines the dummy config which will be sent from server
};
var tempArr = [];
for (i in configDummy) {
console.log(i + '=' + configDummy[i]);
tempArr.push(Ext.decode(configDummy[i]));

}

Ext.QuickTips.init();

var myTest = new Test.DynaForm({
fieldSetConfig: tempArr,
renderTo: document.body
});
});
</script>

</body>
</html>