PDA

View Full Version : [2.0][SOLVED] Panel setTitle(title, iconCls) doesn't quite work



seymores
16 Oct 2007, 2:08 AM
Hi all,

I have a problem and I am at lost once again.
I have a TabPanel with a Panel within it (code below)

I am trying to change the inner panel's title and icon. Calling the panel's setTittle('Some new title') works, but setTittle('new title', 'iconClsvalue') does not.
Calling setIconClass('iconClsvalue') gives me the same problem.

The error is:

this.header has no properties
[Break on this error] var hd = this.header.dom;


It seems that the problem only happens if I put the panel within the TabPanel, otherwise it works perfectly.

Did I miss something?
:-|



var tabPanel = new Ext.TabPanel({
region:'center',
id:'tab-panel',
header:true,
deferredRender:false,
activeTab:0,
items: {
title:'Dashboard',
header:true,
id:'dashboard-panel',
iconCls:'dashboardIcon',
layout:'border',
items: [grid,
{
id:'preview-panel',
region:'south',
height:200,
minSize:75,
maxSize:200,
split:true,
collapsible:true,
autoScroll:true,
tbar: [{
text:'Open in New Window',
scope:this,
handler: function(){
var gd = grid.getSelectionModel()
var row = gd.getSelected();
window.open(row.data.link);
}
}
]
}]
}
});

efege
1 Nov 2007, 10:51 AM
Confirmed.

I'm having this problem too. I want to show a loading indicator as a temporary icon while loading the tab's new content. I'm able to change the title to "Loading...", but get the above error message when attempting to specify a class as a second argument to setTitle().

Are we doing something wrong? Is this a bug?

EDIT: I forgot to mention that this gives true in Firebug's console: Ext.getCmp('myTab').header === undefined

Version of Ext: 2.0b1

hendricd
1 Nov 2007, 11:35 AM
There's a bug in Ext.Panel, I believe (from which TabPanel is derived from). Should be:


/**
* Sets the CSS class that provides the icon image for this panel. This method will replace any existing
* icon class if one has already been set.
* @param {String} cls The new CSS class name
*/
setIconClass : function(cls){
if(this.rendered && this.header){
var old = this.iconCls;
this.iconCls = cls;

if(this.frame){
this.header.addClass('x-panel-icon');
this.header.replaceClass(old, this.iconCls);
}else{
var hd = this.header.dom;
var img = hd.firstChild && String(hd.firstChild.tagName).toLowerCase() == 'img' ? hd.firstChild : null;
if(img){
Ext.fly(img).replaceClass(old, this.iconCls);
}else{
Ext.DomHelper.insertBefore(hd.firstChild, {
tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
});
}
}
}
},


Should not be able to set iconCls on a non-existant header.

efege
1 Nov 2007, 11:44 AM
Should not be able to set iconCls on a non-existant header.

OK, but... in my case a title has been specified in the Panel config, so the header should exist. I even added header: true, which is redundant, but the error is still there. :-/

hendricd
1 Nov 2007, 12:22 PM
@seymores - What call are making to set the title? It would need to be something like:

Ext.getCmp('dashboard-panel').setTitle('something','iconDashboard');

but, in your items collection (missing the [ BTW ) you'll need to tell it what xtype:'panel' to put in there.

var tabPanel = new Ext.TabPanel({
region:'center',
id:'tab-panel',
header:true,
deferredRender:false,
activeTab:0,
items: [{
title:'Dashboard',
header:true,
xtype:'panel',
id:'dashboard-panel',
iconCls:'dashboardIcon',
layout:'border',
items: [grid,
{
id:'preview-panel',
region:'south',
height:200,
minSize:75,
maxSize:200,
split:true,
collapsible:true,
autoScroll:true,

efege
1 Nov 2007, 12:25 PM
Thanks hendricd for helping. I'm not using setTitle with a TabPanel, but with a Panel (ok, I think of it as a "tab", but I know it's a Panel ;) )

These are the relevant portions of my code:



tabPanel.add({
id: 'LCTab',
contentEl: element,
title: 'LC Catalog',
autoScroll: true
});




Ext.getCmp('LCTab').setTitle('Loading...', 'tab-loading');


Everything is rendered OK, but as I said above, Ext.getCmp('LCTab').header is undefined.

As a side comment: I expect headers to behave in a "special" way in the case of tabs (since they don't look like ordinary panels), but I'm pretty sure that this need of setting a loading indicator for the tab will be very common as more people develop Ajax apps, so there should be an easy way to accomplish this with Ext...

So, am I missing something here?

hendricd
1 Nov 2007, 12:29 PM
@efege: trying adding xtype:panel. It doesn't know that you havewant an Ext.Panel in there.

efege
1 Nov 2007, 12:48 PM
xtype added:



tabPanel.add({
id: 'LCTab',
xtype: 'panel',
contentEl: element,
title: 'LC Catalog',
autoScroll: true
});


Firebug:



>>> Ext.getCmp('LCTab')
Object initialConfig=Object id=LCTab xtype=panel
>>> Ext.getCmp('LCTab').header === undefined
true


:-/

efege
1 Nov 2007, 1:08 PM
tabPanel.add({
id: 'LCTab',
contentEl: element,
title: 'LC Catalog',
header: true,
autoScroll: true
});




>>> Ext.getCmp('LCTab')
Object initialConfig=Object id=LCTab
>>> Ext.getCmp('LCTab').title
"LC Catalog"
>>> Ext.getCmp('LCTab').header === undefined
true


Using header: true is redundant when there's already a title: 'something'; anyway I had already tried that, to no avail.

hendricd
1 Nov 2007, 1:12 PM
Yes I'm testing with tabs.html example and noticing the same behavior -- narrowing it down tho....

hendricd
1 Nov 2007, 5:04 PM
Based on my investigation for TabPanel items(Ext.Panels), if you want to render a (true) header to a TabPanel Item, you must specify it as one of the Container-rendered elements list (by design or flaw?) :



tabPanel.add({
id: 'LCTab',
header:true,
title:'something',
elements:'body,header',
contentEl: element,
title: 'LC Catalog',
autoScroll: true
});
The reason why panel.setTitle works without specifying the icon class (and in the absence of panel.header) is that TabPanel subscribes to the panels' setTitle event regardless of the panel's ownership of a header, and when you don't pass the iconCls, it doesn't call setIconClass(the bug orginally reported).

My previous post regarding Panel: setIconClass is a BUG, because it should not try to insert classname's or images on a header that doesn't exist.

I'm hoping someone can bring these issues to Dev's attention, as I will be out of reach for a few days..

Good Luck with it. I'll follow up upon return.

noamdishon
8 Nov 2007, 2:12 AM
did someone open a bug on this?
It's still not working on RC1

deviltje
4 Dec 2007, 1:13 AM
It's still not working in the final (released today).
I'll try to find (or fill ) a bug

keypoint
31 Dec 2007, 10:50 PM
Need this too...

hendricd
5 Jan 2008, 6:12 AM
Here is the fix (http://extjs.com/forum/showthread.php?p=81017#post81017).

jameyg
5 Jan 2008, 2:59 PM
the main issue here is that the TabStrip isn't really part of the Tab item Panel (although logically the Tab item Panel's header should be same as the Tab thingy -- anything else could/should be considered a bug).

The way TabPanel handles setTitle() on the individual Tab items hints at a workaround though (wow -- TabPanel and "a tab Panel" is confusing)...at least for the limited use cases I've come across.


fixedTabItemSetIconClass = function(tabItem, iconCls) {
var oldIconClass = tabItem.iconCls;
var tabSpan = Ext.fly(tabItem.ownerCt.getTabEl(tabItem)).child('span.x-tab-strip-text');
tabSpan.removeClass(oldIconClass);
tabItem.iconCls = iconCls;
tabSpan.addClass(iconCls);
}

// example
new Ext.TabPanel({
renderTo: Ext.getBody(),
items: [
Panel1 = new Ext.Panel({title: 'Panel 1', iconCls: 'original'}),
Panel2 = new Ext.Panel({title: 'Panel 2', iconCls: 'original'})
],
buttons: [
{
text:'click',
handler: function() {
fixedTabItemSetIconClass(Panel1, 'loading');
}
}
]
});


I'm lazy right now, but with a little work this should be easy enough to Ext.extend() TabPanel's initTab() to "patch" the tab item Panel's setIconClass() with this workaround.

Was a bug filed? I can't find it.

jameyg
6 Jan 2008, 1:30 PM
ignore the workaround...here's a "hot patch" to TabPanel itself. Once again, very limited testing, but it worked well enough for my use case (which is basically putting a bunch of GridPanels on tabs and changing the tab's icon if the underlying store is being loaded...) -- I'll assume it'll work for Tabs generated from markup, but I haven't tested that.

NOTE that since we're patching the Panel's setIconClass() method, this fix also works for the setTitle(title, iconCls) method.



Ext.override(Ext.TabPanel, {
// stash this away in TabPanel's prototype for the heck of it (oh...and convenient caching)
_initTabIconClassPatchFly: function(iconCls) {
var oldIconClass = this.iconCls;
var tabSpan = Ext.fly(this.ownerCt.getTabEl(this)).child('span.x-tab-strip-text');
tabSpan.removeClass(oldIconClass);
this.iconCls = iconCls;
tabSpan.addClass(iconCls);
},
// instead of copy/pasting, just grab the current implementation of TabPanel.initTab() for later
_initTabBeforeSetIconClassPatch: Ext.TabPanel.prototype.initTab,
// now override initTab()
initTab: function(item, index) {
// call the original implementation of initTab()
this._initTabBeforeSetIconClassPatch(item, index);
// and patch the Tab item's setIconClass method.
item.setIconClass = this._initTabIconClassPatchFly;
}
});


for anyone struggling w/ a "patching" strategy, put the above code snippet in a file called ext-patches.js and SCRIPT it after the rest of the ext stuff.

aconran
8 Jan 2008, 11:39 AM
Moved this thread from Help to Bugs at the request of Doug Hendricks.

Is the problem here that setIconClass does not work properly when used in a TabPanel because there is actually no header created?

hendricd
8 Jan 2008, 11:49 AM
You got it.

aconran
8 Jan 2008, 11:58 AM
Any chance someone can put together a sample case that would drop into examples/ so that I can do some testing and get this fixed in SVN?

jameyg
10 Jan 2008, 7:46 AM
Is the problem here that setIconClass does not work properly when used in a TabPanel because there is actually no header created?

For clarity, the _original_ issue in this thread isn't that Ext.TabPanel.setIconClass() fails (this itself is a semi-bug since TabPanel.setIconClass() should be a no-op -- TabPanel's themselves logically don't have a header, so both setTitle() and setIconClass() don't make sense), the issue is that calling setIconClass() on any of the TabPanel's items fails (these guy's technically might not have a header...but they do logically...it's the tab thingum itself).

tangent -- don't follow up to this part....titlechange is a semi-weird event in the first place, but why no iconclasschanged (or a more generic "panelpropertychange(property, newValue, oldValue)")

aconran
10 Jan 2008, 11:04 AM
Thanks for clarifying. If someone could provide us with a sample that illustrates the issue it would help us to make sure we get it resolved in SVN/for a future release.

hendricd
10 Jan 2008, 2:42 PM
@aconran -- I'll try to package a drop-in for you to look at.

hendricd
10 Jan 2008, 3:15 PM
OK, Paste this bad-boy into /examples/tabs \:D/

The problem:

I have a TabPanel with a Panel within it (code below)

I am trying to change the inner panel's title and icon. Calling the panel's setTittle('Some new title') works, but setTittle('new title', 'iconClsvalue') does not.
Calling setIconClass('iconClsvalue') gives me the same problem.

The error is:

this.header has no properties
[Break on this error] var hd = this.header.dom;


It seems that the problem only happens if I put the panel within the TabPanel, otherwise it works perfectly.

Tab2 should Error out. Tab3 has the proposed fix.



<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Advanced Tabs</title>
<link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />

<!-- GC -->
<!-- LIBS -->
<script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
<!-- ENDLIBS -->

<script type="text/javascript" src="../../ext-all.js"></script>

<link rel="stylesheet" type="text/css" href="tabs-example.css" />

<!-- Common Styles for the examples -->
<link rel="stylesheet" type="text/css" href="../examples.css" />
</head>
<body>
<script type="text/javascript" src="../examples.js"></script><!-- EXAMPLES -->
<script type="text/javascript" >

Ext.RepairedPanel = Ext.extend(Ext.Panel,{
/**
* Sets the CSS class that provides the icon image for this panel. This method will replace any existing
* icon class if one has already been set.
* @param {String} cls The new CSS class name
*/
setIconClass : function(cls){
if(this.rendered && this.header){
var old = this.iconCls;
this.iconCls = cls;

if(this.frame){
this.header.addClass('x-panel-icon');
this.header.replaceClass(old, this.iconCls);
}else{
var hd = this.header.dom;
var img = hd.firstChild && String(hd.firstChild.tagName).toLowerCase() == 'img' ? hd.firstChild : null;
if(img){
Ext.fly(img).replaceClass(old, this.iconCls);
}else{
Ext.DomHelper.insertBefore(hd.firstChild, {
tag:'img', src: Ext.BLANK_IMAGE_URL, cls:'x-panel-inline-icon '+this.iconCls
});
}
}
}
}
});
Ext.reg('repairedpanel', Ext.RepairedPanel );


Ext.onReady(function(){

var tabs = new Ext.TabPanel({
renderTo:'tabs',
resizeTabs:true, // turn on tab resizing
minTabWidth: 115,
tabWidth:135,
enableTabScroll:true,
width:600,
height:250,
defaults: {autoScroll:true}
});

// tab generation code
var index = 0;
while(index < 3){
addTab(index==2?'repairedpanel':'panel');
}
function addTab(xtype){
tabs.add({
title: 'New Tab ' + (++index),
iconCls: 'tabs',
id:'tab'+index,
xtype: xtype || 'panel',
html: 'Tab Body :Ext.'+xtype + ' ' + (index) + '<br/><br/>',

closable:true
}).show();
}

new Ext.Button({
text: 'Set Title Tab 1',
handler: function(){ Ext.getCmp('tab1').setTitle('GoodSoFar');}
}).render(document.body, 'tabs');


new Ext.Button({
text: 'Set Title/IconClass Tab 2',
handler: function(){ Ext.getCmp('tab2').setTitle('WillBarf','tabs');}
}).render(document.body, 'tabs');

new Ext.Button({
text: 'Set Title/IconClass Tab 3',
handler: function(){ Ext.getCmp('tab3').setTitle('Yippee Success!','tabs');}
}).render(document.body, 'tabs');
});
</script>
<h1>Tab Title/Iconclass Fix</h1>

<div id="tabs" style="margin:15px 0;"></div>
</body>
</html>

hendricd
14 Jan 2008, 5:08 AM
bump

brian.moeskau
14 Jan 2008, 9:12 AM
Hey Doug,

Aaron is not 100% available over the next few days so may not be checking the forums, but I know this thread is on his list. One of us will get back to you soon. Thanks!

RWaters
14 Jan 2008, 11:22 AM
Fixed in SVN Rev 1541

aparajita
17 Jan 2008, 1:39 PM
Fixed in SVN Rev 1541

Not quite...trying to set the icon class is now a no op, which is better than causing an error, but it means that setTitle() does not work as advertised. Here is what is necessary to make setTitle() work with an icon class:

First, change Panel.setTitle() to:


setTitle : function(title, iconCls){
this.title = title;
if(this.header && this.headerAsText){
this.header.child('span').update(title);
}
var oldIconCls = this.iconCls;
if(iconCls){
this.setIconClass(iconCls);
}
this.fireEvent('titlechange', this, title, oldIconCls);
return this;
},

Then, change TabPanel.onItemTitleChanged() to:


onItemTitleChanged : function(item, title, oldIconCls){
var el = this.getTabEl(item);
if(el){
var span = Ext.fly(el).child('span.x-tab-strip-text');
span.update(title);
if(oldIconCls){
span.replaceClass(oldIconCls, item.iconCls);
}
}
},

Works like a charm! :D

RWaters
17 Jan 2008, 2:02 PM
Thanks for the update, I'll check it out.

jameyg
17 Jan 2008, 5:00 PM
Works like a charm! :D

Not sure if this is the solution either since it abandons the standalone Panel.setIconClass() and forces one to use the Panel.setTitle() variant. Also, having Ext.TabPanel register event handlers for the added "tab panels" seems like at best a fragile hack and at worst a bug (try calling Panel.purgeListeners() on a "tab panel" -- a bunch of tab features get disabled -- I'll admit, however, that purgeListeners() exists more for academic reasons than useful ones...)

It seemed hacky, so I originally hated the "hot patch" type of fix I suggested earlier in this thread (i.e. point the tab item's setIconClass() to a different function when it gets added to a TabPanel). But now I'm wondering if it can be effectively tweaked into the Ext.TabPanel's onAdd()/Remove() methods (my "fix" didn't include support for restoring the original setIconClass() implementation on TabPanel.remove() -- that's easy enough to add -- but the current implementation apparently has this same "doesn't clean up on remove" issue). This could arguably be better than an event approach because it doesn't have the purgeListeners() side effect.

Ultimately, the best approach seems to be one where the header exists on Panels when they're used as tab items (the header would act as the tab thingum -- just rendered differently). There's a related (albeit unfiled) bug related to moving a Panel from a RegularContainer to a TabPanelContainer (yup -- the Panel header is unexpectedly there...)

6dust
29 Apr 2008, 6:52 AM
Doesn't seem like this thread has been updated in a while, and I wouldn't consider it [SOLVED], as in the new release, 2.1, setIconClass still doesn't update the icon, nor does changing the iconCls via the second argument of setTitle.

It looks like ExtJS is gaining a lot of popularity with this latest release, which is great. It also seems like there are several users who are running into this problem lately, so I'm hoping that giving this thread a bump will help.

BTW, aside from this bug, I have absolutely fallen in love with ExtJS after a week or so of development. Great work!

BUMP!

6dust
1 May 2008, 9:28 AM
It was probably implementation error on my part, but I couldn't seem to get hendricd's or aparajita's fixes to work. jameyg's fix worked for me for updating tabs that already had an iconCls, but didn't work for adding an icon that didn't previously have one (the title of the tab wouldn't bump over for the icon, so they overlapped).

Added a few lines to jameyg's code that seem to handle that case. I'm sure there are better ways than chaining 4 parent() calls together, but it functions.

Removed parent() calls; just needed some food to clear my head :)


// fix for setIconClass
Ext.override(Ext.TabPanel, {
// stash this away in TabPanel's prototype for the heck of it (oh...and convenient caching)
_initTabIconClassPatchFly: function(iconCls) {
var oldIconClass = this.iconCls;
var flyTabEl = Ext.fly(this.ownerCt.getTabEl(this));
var tabSpan = flyTabEl.child('span.x-tab-strip-text');
tabSpan.removeClass(oldIconClass);
this.iconCls = iconCls;
tabSpan.addClass(iconCls);
// new code to bump over title for icon
if(iconCls == "")
flyTabEl.removeClass('x-tab-with-icon');
else
flyTabEl.addClass('x-tab-with-icon');
},
// instead of copy/pasting, just grab the current implementation of TabPanel.initTab() for later
_initTabBeforeSetIconClassPatch: Ext.TabPanel.prototype.initTab,
// now override initTab()
initTab: function(item, index) {
// call the original implementation of initTab()
this._initTabBeforeSetIconClassPatch(item, index);
// and patch the Tab item's setIconClass method.
item.setIconClass = this._initTabIconClassPatchFly;
}

});

derbbre
4 Jun 2008, 10:35 AM
I use this override, using my own method name (setIconCls - don't see why it chould be iconCls in some places and IconClass in others, anyway) as I don't want to step on the original method if/when it gets fixed:



Ext.override(Ext.Panel, {
setIconCls: function(i){
Ext.fly(this.ownerCt.getTabEl(this)).child('.x-tab-strip-text').replaceClass(this.iconCls, i);
this.setIconClass(i);
}
});


Allows on-the-fly icon replacement as such:


Ext.getCmp('myTab').setIconCls('new-class');

mjlecomte
25 Oct 2008, 3:33 AM
Just cross linking this thread to a related (unresolved) bug report and patch (http://extjs.com/forum/showthread.php?p=193156#post193156).

NoahK17
29 Dec 2009, 9:10 AM
Stumbled upon this thread, but in the latest release 3.1.0, this issue is fixed. Panel.setIconClass('new-icon-class'); works just fine. Cheers!