PDA

View Full Version : Rendering issue whern toggle readonly on DateField or ComboBox in tabpanel



cgi-bin
21 Apr 2011, 6:07 AM
Hi,
I have a page with a form that utilizes a tabpanel to separate groups of fields. All of the fields in the form are initially set to readOnly. I toggle the readOnly for all the fields via Edit/Cancel buttons.

Initially all fields are rendered correctly (deferredRender:false). Wen I first toggle the fields to readOnly:false, they still look fine, however, when I toggle them back to readOnly:true, the ComboBox and DateField fields "collapse". Subsequent toggles, do not fix them back to full size (the readonly part works, just how they are rendered).


var allowEdit = false;
var setReadOnly = function(item, index, length)
{
item.setReadOnly(!allowEdit);
}

var clearForm = function()
{
myForm.getForm().reset();

myForm.newBttn.enable();
myForm.cancelBttn.disable();
myForm.saveBttn.disable();

allowEdit = false;
myForm.getForm().items.each(setReadOnly);
}

var editForm = function()
{
myForm.newBttn.disable();
myForm.cancelBttn.enable();
myForm.saveBttn.enable();

allowEdit = true;
myForm.getForm().items.each(setReadOnly);
}

var locStore = new Ext.data.JsonStore
({
autoDestroy:true,
autoLoad:true,
url:'/asset/getLocations/',
root:'locations',
idProperty:'location_id',
fields:
[
{name:'location_id'},
{name:'location_name'}
]
});

var locCB = new Ext.form.ComboBox
({
store: locStore,
name:'location',
fieldLabel:'Location',
displayField:'location_name',
valueField:'location_id',
typeAhead:true,
mode:'local',
triggerAction:'all',
emptyText:'Choose...',
selectOnFocus:true,
allowBlank:false,
forceSelection:true
});

var myForm = new Ext.FormPanel
({
title:'Details',
flex: 0,
bodyStyle: 'background-color: #DFE8F6;',
padding: 2,
labelAlign:'right',
labelWidth:85,
url:'/asset/modify/',
frame: false,
defaults:
{
anchor:'0',
readOnly:true
},
items:
[{
name:'myindex',
xtype:'hidden'
},{
xtype:'container',
layout:'column',
bodyStyle: 'background-color: #DFE8F6;',
border:false,
defaults:
{
bodyStyle: 'background-color: #DFE8F6;',
columnWidth:.5,
layout:'form',
border:false,
labelWidth:85,
defaults:
{
anchor:'0',
readOnly:true,
xtype:'textfield'
}
},
items:
[{
items:
[
fieldLabel:'Manufacturer',
name: 'manuf'
]
},
{
items:
[{
fieldLabel:'Model',
name: 'model'
}]
}]
},
{
xtype:'textarea',
fieldLabel:'Description',
name:'description',
height:40
},
{
xtype:'tabpanel',
activeTab:0,
height:130,
deferredRender:false,
defaults:
{
layout:'form',
bodyStyle: 'background-color: #DFE8F6;',
labelWidth:100,
defaults:
{
anchor:'0',
readOnly:true,
xtype:'textfield'
}
},
items:
[{
title:'Location',
items:
[{
xtype:'container',
layout:'column',
bodyStyle: 'background-color: #DFE8F6;',
border:false,
defaults:
{
bodyStyle: 'background-color: #DFE8F6;',
columnWidth:.5,
layout:'form',
border:false,
labelWidth:100,
defaults:
{
anchor:'0',
readOnly:true,
xtype:'textfield'
}
},
items:
[{
items:
[
locCB,
{
fieldLabel:'Cabinet',
name:'cabinet'
},{
fieldLabel:'U Start',
name:'ustart'
}]
},{
items:
[{
fieldLabel:'Room/Cube',
name:'roomcube'
},{
fieldLabel:'Enclosure Bay',
name:'bay'
},{
fieldLabel:'U End',
name:'uend'
}]
}]
}]
},{
title:'Dates',
items:
[{
xtype:'container',
layout:'column',
bodyStyle: 'background-color: #DFE8F6;',
border:false,
defaults:
{
bodyStyle: 'background-color: #DFE8F6;',
columnWidth:.5,
layout:'form',
border:false,
labelWidth:100,
defaults:
{
anchor:'0',
readOnly:true,
xtype:'datefield'
}
},
items:
[{
items:
[{
fieldLabel:'Purchased',
name:'date1'
},{
fieldLabel:'Received',
name:'date2'
}]
},{
items:
[{
fieldLabel:'Support Start',
name:'date3'
},{
fieldLabel:'Support End',
name:'date4'
}]
}]
}]
}]
}],
tbar:
[{
ref:'../newBttn',
iconCls:'add_icon',
tooltip:'Add a New Asset',
text:'New',
handler:function()
{
clearForm();

editForm();
}
},
'-',
{
ref:'../cancelBttn',
iconCls:'cancel_icon',
tooltip:'Clear Form<br>Any unsaved changes will be discarded!',
text:'Cancel',
disabled:true,
handler:function()
{
clearForm();
}
},
'->',
{
ref:'../saveBttn',
text:'Save Changes',
iconCls:'disk_icon',
disabled:true,
handler:function()
{
var fm = myForm.getForm();
if (fm.isValid())
{
alert('Not Implemented Yet...');
}
else
{
alert('Please correct the indicated errors first!');
}
}
}]
});

cgi-bin
27 Apr 2011, 6:00 AM
Still having the issue, I have tried scattering forceLayout:true and deferredRender:false in various places with no luck.

Below is a self contained example... when it loads, click each of the 4 tabs, and each field is correctly shown in readOnly mode. Click back to the 1st tab, then click the "edit" button, now any of the ComboBox or DateField fields in the tabs (2-4) will be collapsed. What I think is happening, is that it is loosing the anchor:0 when it is changing the render. If I set a static width on the items, they work fine (see TabField3_3). It initially renders with the anchor:0 but then reverts to (and stays with) the fixed width. Also, explicitly setting anchor:0 on the individual fields doesn't make a difference vs. having it set in the defaults:{} (see TabField3_4).

If you don't initially cycle through the tabs, clicking the edit, then checking the tabs, they are correctly rendered the 1st time, but then clicking cancel, and checking again, they are messed up. Also, if a tab is visible with the fields that would usually have a problem, they stay fine until a edit/cancel cycle is done without that tab being visible.

The ComboBox and DateField fields work fine in the non-tab section of the form (I assume since they stay visible?). Also, tab4 shows its not a problem with the fields being nested in the container/column layout.

Honestly it seems like deferredRender:false isn't working correctly, as when I first click each tab, there seems to be a flash of un-rendered layout... subsequent clicks of that tab show instantly. Also, it seems that until a tab is clicked, when I use the save button, its not posting the fields from those tabs (I need to further investigate, and will probably open a separate thread on that issue)

I haven't (yet) tried to find a way to force a re-render re-layout when a tab comes into view or when doing the readOnly toggle (probably an ideal place since I'm already looping through each item in the form). I wanted to see if this is maybe a bug or I have something miss-configured 1st.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>TEST</title>
<!-- Style Sheets -->
<link rel="stylesheet" type="text/css" href="/css/ExtJS/ext-all.css" />

<!-- JavaScript Libraries -->
<script type="text/javascript" src="/js/ExtJS/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="/js/ExtJS/ext-all.js"></script>

<script type="text/javascript">
Ext.BLANK_IMAGE_URL = '/img/ExtJS/default/s.gif';

var allowEdit = false;
var setReadOnly = function(item, index, length)
{
item.setReadOnly(!allowEdit);
}

var clearForm = function()
{
myForm.getForm().reset();

myForm.newBttn.enable();
myForm.cancelBttn.disable();
myForm.saveBttn.disable();

allowEdit = false;
myForm.getForm().items.each(setReadOnly);
}

var editForm = function()
{
myForm.newBttn.disable();
myForm.cancelBttn.enable();
myForm.saveBttn.enable();

allowEdit = true;
myForm.getForm().items.each(setReadOnly);
};

var submitForm = function(s)
{
if ((s == 'SAVE') && !myForm.getForm().isValid())
{
Ext.Msg.alert('Incomplete Form', 'Please correct the indicated errors first!');

return false;
}

myForm.getForm().submit
({
waitMsg:'Please wait...',
params:
{
do_action:s
},
success:function(f, a)
{
if (a && a.result)
{
if (a.result.message)
{
Ext.Msg.alert('Success', a.result.message);
}

clearForm();
}
else
{
Ext.Msg.alert('Failure', 'Unknown Result');
}
},
failure:function(f, a)
{
if (a && a.result && a.result.message)
{
Ext.Msg.alert('Failure', a.result.message);
}
else if (a && a.response && a.response.responseText)
{
Ext.Msg.alert('Failure', a.response.responseText);
}
else
{
Ext.Msg.alert('Failure', 'Unknown Error');
}
}
});
};

var myStore = new Ext.data.ArrayStore
({
autoDestroy:true,
idProperty:'my_id',
fields:
[
{name:'my_id'},
{name:'my_name'}
],
data:
[
[1, 'item1'],
[2, 'item2'],
[3, 'item3'],
[4, 'item4'],
[5, 'item5']
]
});

var myCB1 = new Ext.form.ComboBox
({
store:myStore,
name:'field1_text',
hiddenName:'field1',
fieldLabel:'Field1 (cb)',
displayField:'my_name',
valueField:'my_id',
typeAhead:true,
mode:'local',
triggerAction:'all',
emptyText:'Choose...',
selectOnFocus:true,
forceSelection:true
});

var myCB2 = new Ext.form.ComboBox
({
store:myStore,
name:'tabfield2_1_text',
hiddenName:'tabfield2_1',
fieldLabel:'TabField2_1 (cb)',
displayField:'my_name',
valueField:'my_id',
typeAhead:true,
mode:'local',
triggerAction:'all',
emptyText:'Choose...',
selectOnFocus:true,
forceSelection:true
});

var myCB3 = new Ext.form.ComboBox
({
store:myStore,
name:'tabfield4_1_text',
hiddenName:'tabfield4_1',
fieldLabel:'TabField4_1 (cb)',
displayField:'my_name',
valueField:'my_id',
typeAhead:true,
mode:'local',
triggerAction:'all',
emptyText:'Choose...',
selectOnFocus:true,
forceSelection:true
});

var myForm = new Ext.FormPanel
({
bodyStyle:'background-color: #DFE8F6;',
padding:2,
labelAlign:'right',
labelWidth:85,
url:'/test/testSave/',
frame:false,
defaults:
{
anchor:'0',
readOnly:true
},
items:
[{
name:'my_index',
xtype:'hidden',
value:'1234'
},{
xtype:'container',
layout:'column',
bodyStyle:'background-color: #DFE8F6;',
border:false,
defaults:
{
bodyStyle:'background-color: #DFE8F6;',
columnWidth:.5,
layout:'form',
border:false,
labelWidth:85,
defaults:
{
anchor:'0',
readOnly:true,
xtype:'textfield'
}
},
items:
[{
items:
[
myCB1,
{
fieldLabel:'Field3',
name:'field3'
}]
},
{
items:
[{
fieldLabel:'Field2 (df)',
name:'field2',
xtype:'datefield'
},{
fieldLabel:'Field4',
name:'field4'
}]
}]
},
{
xtype:'tabpanel',
activeTab:0,
deferredRender:false,
defaults:
{
autoHeight:true,
bodyStyle:'padding:2px; background-color: #DFE8F6;',
layout:'form',
labelWidth:100,
defaults:
{
anchor:'0',
readOnly:true
}
},
items:
[{
title:'Tab1',
items:
[{
xtype:'container',
layout:'column',
bodyStyle:'background-color: #DFE8F6;',
border:false,
defaults:
{
bodyStyle:'background-color: #DFE8F6;',
columnWidth:.5,
layout:'form',
border:false,
labelWidth:100,
defaultType:'textfield',
defaults:
{
anchor:'0',
readOnly:true
}
},
items:
[{
items:
[{
fieldLabel:'TabField1_1',
name:'tabfield1_1'
},{
fieldLabel:'TabField1_3',
name:'tabfield1_3'
}]
},{
items:
[{
fieldLabel:'TabField1_2',
name:'tabfield1_2'
},{
fieldLabel:'TabField1_4',
name:'tabfield1_4'
}]
}]
}]
},{
title:'Tab2',
items:
[{
xtype:'container',
layout:'column',
bodyStyle:'background-color: #DFE8F6;',
border:false,
defaults:
{
bodyStyle:'background-color: #DFE8F6;',
columnWidth:.5,
layout:'form',
border:false,
labelWidth:100,
defaultType:'textfield',
defaults:
{
anchor:'0',
readOnly:true
}
},
items:
[{
items:
[
myCB2,
{
fieldLabel:'TabField2_3',
name:'tabfield2_3'
}]
},{
items:
[{
fieldLabel:'TabField2_2',
name:'tabfield2_2'
},{
fieldLabel:'TabField2_4',
name:'tabfield2_4'
}]
}]
}]
},{
title:'Tab3',
items:
[{
xtype:'container',
layout:'column',
bodyStyle:'background-color: #DFE8F6;',
border:false,
defaults:
{
bodyStyle:'background-color: #DFE8F6;',
columnWidth:.5,
layout:'form',
border:false,
labelWidth:100,
defaultType:'datefield',
defaults:
{
anchor:'0',
readOnly:true
}
},
items:
[{
items:
[{
fieldLabel:'TabField3_1 (df)',
name:'tabfield3_1'
},{
fieldLabel:'TabField3_3 (df)',
name:'tabfield3_3',
width:100
}]
},{
items:
[{
fieldLabel:'TabField3_2 (df)',
name:'tabfield3_2'
},{
fieldLabel:'TabField3_4 (df)',
name:'tabfield3_4',
anchor:'0'
}]
}]
}]
},{
title:'Tab4',
items:
[
myCB3,
{
fieldLabel:'TabField4_2 (df)',
name:'tabfield4_2',
xtype:'datefield'
}]
}]
}],
tbar:
[{
ref:'../newBttn',
text:'Edit',
handler:function()
{
editForm();
}
},
'-',
{
ref:'../cancelBttn',
text:'Cancel',
disabled:true,
handler:function()
{
clearForm();
}
},
'->',
{
ref:'../saveBttn',
text:'Save Changes',
disabled:true,
handler:function()
{
submitForm('SAVE');
}
}]
});

Ext.onReady
(
function()
{
Ext.QuickTips.init();

myForm.render(document.body);
}
);
</script>
</head>
<body style="margin:5px;"></body>
</html>

Graveworm
27 Apr 2011, 6:12 AM
maybe a doLayout() in a beforeTabChange-listener help:


{
xtype:'tabpanel',
// (...)
listeners:{
scope: this,
'beforetabchange':function(tabPanel, newTab, currentTab)
{
newTab.doLayout();
}
}
}

cgi-bin
27 Apr 2011, 6:25 AM
maybe a doLayout() in a beforeTabChange-listener help:
Thanks for the suggestion, however it doesn't make a difference... I even used doLayout(false, true) to do non-shallow and force the layout even if hidden.

cgi-bin
27 Apr 2011, 7:31 AM
I tried in both beforetabchange and tabchange listeners:

newTab.doLayout(false, true);
tabPanel.doLayout(false, true);
myForm.doLayout(false,true);
myForm.getForm().applyToFields({anchor:'0'});

I also tied the applyToFields followed by a doLayout.

None of these worked.

Seeing that both ComboBox and DateField extend TriggerField which extends TextField, I'm thinking the problem lies in TriggerField (since TextField works fine).

Looking at the source for TriggerField it overrides the setReadOnly (originally defined in Field). The new fn calls updateEditState which is calling the onResize handler and its snowballing from there...

Perhaps someone with more expertise in how the various resizing functions work/cascade trace through this and see where the correct width (from anchor) is getting lost?

friend
27 Apr 2011, 8:59 AM
I've seen similar problems when components didn't have a declared width, or the container didn't properly specify a layout/height.

Experiment with fixed width's on the fields in question and also look through the container hierarchy and verify that everything has a declared layout.

If no luck there, try setting a fixed height on your form.

cgi-bin
27 Apr 2011, 10:29 AM
one odd thing i did accidentally figure out: If the form is in a viewport and you resize the window, it "fixes" the fields (at least until the next edit/cancel). I've tried firing the resize event on the viewport in the tabchange listener, and it didn't seem to do anything (also set monitorResize:true).
Ext.onReady
(
function()
{
Ext.QuickTips.init();

var vp = new Ext.Viewport
({
layout:'fit',
items:
[
myForm
]
});
}
);

All of the nested containers that can have a layout have a layout defined. I am trying to avoid having to use fixed width fields so the layout "flows" and looks uniform.

I did find the layoutOnTabChange config for the tab panel, setting that to true, seems to have the effect of pre-laying out the tab the 1st time (the isn't a flash of un-rendered fields at 1st), but the core problem is still occurring.

cgi-bin
27 Apr 2011, 10:43 AM
was looking at examples at: http://examples.extjs.eu/?ex=tabsinform

hideMode:'offsets' in the defaults{} of the tabPanel seems to fix the problem... However, I still think this is probably a bug somewhere in the TriggerField handling of setReadOnly().