PDA

View Full Version : Correct way to removeAll() and add() so that controller refs work



dcoan604
20 Feb 2012, 11:30 AM
Hi guys.

I have a typical border layout with menu in west region and content container in center.

I am using removeAll() and add() to swap panels in and out of the content container.

I have a controller which has refs set up to refer to one of the panels (ie ContactsPanel) that gets added to the content container.

Every panel that gets add()ed to the content container is set to autoDestroy = true.
Prior to add()ing a panel, it is first created using Ext.create (not xtype).

The FIRST time I add ContactsPanel to the content container, it is created using Ext.create... and my controller can correctly reference the panel (and its children) using the controllers getters. (ie getContactsPanel)

However, if I removeAll() and then later re- add() ContactsPanel... the following occurs:

1. ContactsPanel is created and displays in the content-container
2. the controller getter functions still return a reference to ContactsPanel using getContactsPanel()
3. But ContactsPanel is "destroyed"......ie getContactsPanel().isDestroyed== true

I have tried using Ext.create instead of xtype because I thought that would create a new instance of ContactsPanel. And I have even tried using Ext.create for all the children (items, dockedItems, etc) within ContactsPanel, and then adding them within ContactPanel.init() function.

My getters in the controller all work the first time ContactsPanel is added, but return references to destroyed objects when ContactsPanel is removed and re-added later.

Also, if I use getContactsPanel().query('textfield') to try to refer to textfields in my ContactsPanel it only works the first time. On subsequent 'adds' the query returns empty array []. I assume that is because the textfields were destroyed with my ContactsPanel.....
However, when I Ext.create the ContactsPanel, ContactsPanel.init() runs fine...and within this init I am using Ext.create to also create all of my textfields and add them ContactsPanel.items.


FYI: All of my controllers are setup using the application' controllers[] config.

Without switching to a card panel, what is the correct way to do this?

Thanks in advance.

koko2589
20 Feb 2012, 11:44 AM
text:'RemoveAll', handler: function() { Ext.getCmp("your id").removeAll(true);
}

dcoan604
20 Feb 2012, 12:00 PM
Not sure what you are trying to tell me here.

I get the same results whether I user getCmp to locate my content-container or when I use my MainMenuController which has a ref/getter which references the content-container.

The content-container itself is NOT destroyed... just the panels which are added and removed from it.

I've tried using removeAll(), removeAll(true) and removeAll(false) with no difference.

Once my panel is removed and then re-added to the content-container... my panel controller getters return a destroyed object.



Thanks for trying.

dcoan604
21 Feb 2012, 11:30 PM
Re-reading the docs, I notice that the refs use the query only the first time.
Once they find the referenced object, it is stored in cache... So future calls to the getter are quicker.
However, if the object gets destroyed, the getter doesn't re-query for future instances.

Is there a way to reset the controller somehow, assuming I know when objects get destroyed?

I'm learning about config...
I didn't realize that if you wrap your configs in the config object, that the class will create getters for each of those items. Maybe that is something I need to explore in more detail.

yAdEs
26 Feb 2012, 7:54 AM
Set another ref for that new panel?

vietits
26 Feb 2012, 7:02 PM
@dcoan604,



Re-reading the docs, I notice that the refs use the query only the first time.
Once they find the referenced object, it is stored in cache... So future calls to the getter are quicker.
However, if the object gets destroyed, the getter doesn't re-query for future instances.

Yes, the refs mechanism uses cache to store referenced components for quicker access. However, each time a ref component is destroyed, it will be marked as null in cache. Next time you refer to the component by calling its getter, it will query again (used Ext.ComponentQuery.query()) and put the result in cache again. This way, the ref component getter either returns component if it exists or null if it doesn't exists. Below is the getRef() code in Ext.app.Controller for reference:


...
getRef: function(ref, info, config) {
this.refCache = this.refCache || {};
info = info || {};
config = config || {};


Ext.apply(info, config);


if (info.forceCreate) {
return Ext.ComponentManager.create(info, 'component');
}


var me = this,
selector = info.selector,
cached = me.refCache[ref];


if (!cached) {
me.refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0]; <- Query component if it is not in cache or it is marked as null and then put the result in cache again for next reference.
if (!cached && info.autoCreate) {
me.refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
}
if (cached) {
cached.on('beforedestroy', function() {
me.refCache[ref] = null; <- component is marked as null in ref cache when it is destroyed
});
}
}


return cached;
},
...


I also try to simulate your case. I include here my test code for reference. I don't know whether or not my test case is the same as your case but it works well. I use Ext 4.0.7 and Chrome browser.

1. Application


Ext.application({
name: 'AM',
autoCreateViewport: true,
controllers: [
'Main'
]
});

2. Controller


Ext.define('AM.controller.Main', {
extend: 'Ext.app.Controller',


refs: [{
ref: 'contentContainer',
selector: 'panel[region=center]'
},{
ref: 'contentPanel',
selector: 'panel[region=center] panel'
}],


init: function() {
this.control({
'panel[region=west] button[action=add]': {
click: this.addPanel
},
'panel[region=west] button[action=remove]': {
click: this.removePanel
},
'panel[region=west] button[action=check]': {
click: this.checkPanel
}
});
},
addPanel: function(){
console.log('add panel')
var pnl = Ext.create('Ext.panel.Panel',{
title: 'My Panel',
width: 200,
height: 100,
items: [{
xtype: 'textfield',
name : 'name'
}]
});
var ctn = this.getContentContainer();
ctn.add(pnl)
},
removePanel: function(){
console.log('remove panel')
var ctn = this.getContentContainer();
ctn.removeAll();
},
checkPanel: function(){
console.log('check panel');
var pnl = this.getContentPanel();
if(pnl){
console.log(pnl, pnl.isDestroyed, pnl.query('textfield[name=name]'));
}
}
});

3. Viewport


Ext.define('AM.view.Viewport', {
extend: 'Ext.container.Viewport',
layout: 'fit',
items: [{
xtype: 'panel',
width: 500,
height: 400,
title: 'Border Layout',
layout: 'border',
items: [{
title: 'West Region is collapsible',
region:'west',
xtype: 'panel',
layout: 'vbox',
margins: '5 0 0 5',
width: 200,
collapsible: true,
items: [{
xtype: 'button',
text : 'Add',
action: 'add'
},{
xtype: 'button',
text : 'Remove',
action: 'remove'
},{
xtype: 'button',
text : 'Check',
action: 'check'
}]
},{
title: 'Center Region',
region: 'center',
xtype: 'panel'
}],
}]
});

One more thing, you only need to set autoDestroy to true on container (which default is true) not on its children.

dcoan604
26 Feb 2012, 8:03 PM
Hmmm interesting. I will have to try your example.
I had a problem with doing this in my use case.

When I re-added the panel after it was removed... It was displayed, but some of its docked items were missing, and if I checked the isDestroyed property for the panel, it was true.

In my refs, I was only using the panel's xtype which I assigned using alias:widget.contactdetail. And used in my ref like 'contactdetail'

In my case the panel could be added into the content panel.... Or it could be used in a window (never at the same time though), so I wanted my controller to "find" it whether it was in content container, or in a window... So I used the xtype only (not a full component query). Do u think that might be a source of my issues? Personally I think it may have more to do with my understanding about how classes and objects are instanciated.
I have another thread about my difficulties in that area.

So you are saying that once a component is removed (and destroyed) that the controller will return null using getContactDetail()... But once it is re instanced and re-added to the Dom, that the controller will re-find the new instance and start returning a reference to the newly created instance? That is how I thought it would work, but wasn't getting those results.


I'll have another look when I get a moment and report back. Thanks for the reply.