PDA

View Full Version : Namespace question



cgi-bin
23 Feb 2011, 7:39 AM
The API documentation says that Ext.namespace() returns the namespace object... can I do something like this:
var myNS = Ext.namespace('XXX.tabs.tab-22');
myNS.myStore = new Ext.data.JsonStore({});
myNS.myGrid = new Ext.grid.GridPanel({});
myNS.myForm = new Ext.FormPanel({});
myNS.someFunction = function(){
myNS.myForm.getForm().reset();
};
It "seems" to work, I just want to make sure its actually doing what I think its doing...

Basically what I'm trying to do is ensure each tab in a tab panel operates in its own namespace. I pass a tab-id into each tab as its created, and the back-end code is "cleaner" if I only have to substitute in that variable in the initial namespace creation line, instead of every place the namespace is referenced.

arthurakay
23 Feb 2011, 9:28 AM
Technically, yes you're correct.

However I don't really understand the point. When you create a tab, you're simply creating an instance of some component. That component (e.g. an instance of Panel() ) will already have references and methods to help you access data stores, child components, etc.

What you're doing is creating some literal object (a singleton) with references to the data stores and child components. It would simply be easier to allow the ExtJS component (i.e. your tab) to manage these things for you. At least that's my two cents.

I generally only create namespaces to manage a collection of generic methods or classes (extended from ExtJS components).

cgi-bin
23 Feb 2011, 11:12 AM
Thanks for your reply.

Technically it works... to an extent. However, I ran into a case where because each tab declares "var myNS = ...", I'm getting some cases where things are pointing to the incorrect object.

My "point" is to have each tab have its own "scope", so that if I had two tabs, each with objects called myForm and myGrid, that if I called myForm.reset() in a handler in myGrid in tab2, it only affects the myForm in tab2.

I ran into this problem fairly early on in my development, and turned using a unique namespace for each tab, which did indeed work very well. However, as the application has further progessed, I've found that some tabs would benefit from using the same backend code to generate thier output (using a MVC framework... ie some tabs would share the same view). This led to changing each tab/view from using a staticly set namespace id to using a variable passed in as the id value.

That led me to try to declare the namespace as in my starting example so that the passed in variable only had to be used in one line of the view output, instead of everywhere an object in the namespace is referenced. The code was more "pure" javascript that way and easier to read, without having a mix of PHP echo's and javascript.

Pehaps I should maybe back up and re-look to see if there is a "better" way of structuring my code. I've tried to learn from a lot of the example code given in the ExtJS documentation. It seems there has been a shift from embedding sub-components directly into the items:[] of objects, to defining each major component discretely and then refencing them in the items[]. I'll layout what my design is trying to do, and if anyone has sugestions or tips, I'm very willing to learn new tricks.

Overview: Main page is a border layout with a treepanel in west, and a tabpanel center. When a leaf item is clicked in the treepanel, it opens a new tab in center with the url passed from the treenode and scripts: true set. Each tab url is handled by the back-end MVC framework and the output views for the tabs are usually fairly lightweight PHP scripts that are mostly pure javacript, with only a handful of php echo statements to embed tab_id. A typical tab contains a store/grid, a store/grid and form, or two stores/grids.

An example typical tab output:
<script type="text/javascript">
Ext.namespace('XXX.smtp.show_requests');

XXX.smtp.show_requests.myTab = Ext.ComponentMgr.get('<?php echo $tab_id; ?>');

if (XXX.smtp.show_requests.myTab)
{
XXX.smtp.show_requests.myStore = new Ext.ux.data.PagingJsonStore
({
autoDestroy:true,
autoLoad:
{
params:
{
start: 0,
limit: 25
}
},
url:'/smtp/listRequests/<?php echo $filter; ?>/',
root:'requests',
totalProperty: 'total',
successProperty: 'success',
idProperty: 'requestindex',
sortInfo:
{
field: 'lastmod',
direction: 'ASC'
},
fields:
[
{name:'requestindex'},
{name:'requestdesc'},
{name:'requestor'},
{name:'userinf'},
{name:'hip'},
{name:'hlocation'},
{name:'info'},
{name:'proxyownercomment'},
{name:'smtpowner'},
{
name:'lastmod',
type:'date',
dateFormat:'Y-m-d H:i:s'
}
],
listeners:
{
exception: function(t, type, act, opt, resp, arg)
{
alert('Failed to load data');
}
}
});

XXX.smtp.show_requests.clearForm = function()
{
var fm = XXX.smtp.show_requests.myForm.getForm();
fm.findField('proxyownercomment').setReadOnly(true);
fm.reset();

<?php if ($is_admin) { ?>
XXX.smtp.show_requests.myForm.statusBttnGrp.disable();
<?php } // is_admin ?>

XXX.smtp.show_requests.myForm.cancelBttn.disable();

XXX.smtp.show_requests.myGrid.getSelectionModel().clearSelections();
};

XXX.smtp.show_requests.submitForm = function(s)
{
XXX.smtp.show_requests.myForm.getForm().submit
({
waitMsg:'Please wait...',
params:
{
new_status:s
},
success: function(f, a)
{
if (a && a.result)
{
XXX.smtp.show_requests.clearForm();
XXX.smtp.show_requests.myGrid.getBottomToolbar().doRefresh();

if (a.result.message)
{
Ext.Msg.alert('Success', a.result.message);
}
}
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
{
Ext.Msg.alert('Failure', 'Unknown Error');
}
}
});
};

XXX.smtp.show_requests.myForm = new Ext.FormPanel
({
title:'Request Details',
flex: 0,
bodyStyle: 'background-color: #DFE8F6;',
padding: 2,
labelAlign:'right',
labelWidth:85,
url:'/smtp/modifyRequest/',
frame: false,
defaults:
{
anchor: '0',
readOnly: true
},
items:
[{
name:'requestindex',
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
}
},
items:
[{
items:
[{
fieldLabel:'Requestor',
name:'requestor',
xtype:'textfield'
},{
fieldLabel:'Contact E-Mail',
name:'userinf',
xtype:'textfield'
},{
fieldLabel:'Status',
name:'info',
xtype:'textfield'
}]
},
{
items:
[{
fieldLabel:'IP Address',
name: 'hip',
xtype:'textfield'
},{
fieldLabel:'Location',
name:'hlocation',
xtype:'textfield'
},{
fieldLabel:'Last Update',
name:'lastmod',
xtype:'textfield'
}]
}]
},
{
xtype:'textarea',
fieldLabel:'Reason',
name:'requestdesc'
},
{
xtype:'textfield',
fieldLabel:'Owner',
name:'smtpowner'
},
{
xtype:'textarea',
fieldLabel:'Owner Comment',
name:'proxyownercomment'
}],
fbar:
{
buttonAlign: 'left',
items:
[{
ref:'../cancelBttn',
text:'Cancel',
disabled:true,
tooltip:'Clear the selected record from the form',
handler:function()
{
XXX.smtp.show_requests.clearForm();
}
<?php if ($is_admin) { ?>
},
'->',
{
ref:'../statusBttnGrp',
id:'smtp_requests_admin_buttons',
xtype:'buttongroup',
frame:false,
disabled:true,
items:
[{
text:'Deny Request',
tooltip: 'Reject the request.<br>A comment is required.',
handler: function()
{
var fm = XXX.smtp.show_requests.myForm.getForm();
var c = fm.findField('proxyownercomment');
var v = c.getValue().trim();

if (!v)
{
c.markInvalid('A comment is required');
}
else if (v.length < 10)
{
c.markInvalid('Please enter a more descriptive comment');
}
else
{
XXX.smtp.show_requests.submitForm('DENIED');
}
}
},{
text:'Complete Request',
tooltip: 'Indicate that the request is completed.<br>A comment is optional.',
handler: function()
{
XXX.smtp.show_requests.submitForm('COMPLETE');
}
},{
text:'Complete/Needs FW',
tooltip: 'Indicate that the request is completed, but requires a Firewall Rule change.<br>A comment is optional.',
handler: function()
{
XXX.smtp.show_requests.submitForm('APPLIEDFW');
}
}]
<?php } // is_admin ?>
}]
}
});

XXX.smtp.show_requests.myFilter = new Ext.ux.grid.GridFilters
({
autoReload: false, //don't reload automatically
local: true //only filter locally
});

XXX.smtp.show_requests.myGrid = new Ext.grid.GridPanel
({
//title:'Requests',
flex: 1,
loadMask:true,
store:XXX.smtp.show_requests.myStore,
border:false,
enableColumnHide: false,
enableColumnMove: false,
cm: new Ext.grid.ColumnModel
({
defaults:
{
hideable:false,
resizable:false,
sortable:true,
filter:
{
type: 'string'
}
},
columns:
[{
id:'statusCol',
header:'Status',
dataIndex:'info',
width:75,
filter:
{
type: 'list',
options:
[
'PENDING',
'DENIED',
'APPLIEDFW',
'COMPLETE'
]
}
},
{
id:'rqCol',
header:'Requestor',
dataIndex:'requestor',
width:90
},
{
id:'ceCol',
header:'Contact',
dataIndex:'userinf',
resizable:true
},
{
id:'ipCol',
header:'IP',
dataIndex:'hip',
width:90,
resizable:true
},
{
id:'locCol',
header:'Location',
dataIndex:'hlocation',
width:90,
resizable:true,
filter:
{
type: 'list',
options:
[
'Mechanicsburg',
'Philadephia',
'Pittsburgh'
]
}
},
{
id:'updateCol',
header:'Updated',
xtype: 'datecolumn',
format: 'm/d/Y h:i A',
dataIndex:'lastmod',
width:125,
filter:
{
type: 'date'
}
}]
}),
stripeRows:true,
autoExpandColumn:'ceCol', // contact email
sm: new Ext.grid.RowSelectionModel
({
singleSelect:true,
listeners:
{
rowselect: function(sm, row, rec)
{
var fm = XXX.smtp.show_requests.myForm.getForm();
fm.loadRecord(rec);

<?php if ($is_admin) { ?>
var b = (rec.data.info != 'PENDING');
fm.findField('proxyownercomment').setReadOnly(b);
XXX.smtp.show_requests.myForm.statusBttnGrp.setDisabled(b);
<?php } // is_admin ?>

XXX.smtp.show_requests.myForm.cancelBttn.enable();
}
}
}),
viewConfig:
{
getRowClass: function(rec, i, rp, s)
{
var css = '';

if(rec.data.info == 'PENDING')
{
css = 'x-grid-row-red';
}

return css;
}
},
// paging bar on the bottom
bbar: new Ext.ux.PagingToolbar
({
pageSize: 25,
store: XXX.smtp.show_requests.myStore,
displayInfo: true,
displayMsg: 'Requests {0} - {1} of {2}',
emptyMsg: 'No requests to display',
plugins: [XXX.smtp.show_requests.myFilter]
}),
listeners:
{
sortchange: function(g, s)
{
if (g.getStore().getTotalCount())
{
g.getBottomToolbar().doLoad(0);
}
},
filterupdate: function(f)
{
if (f.grid.getStore().getTotalCount())
{
f.grid.getBottomToolbar().doLoad(0);
}
}
},
plugins: [XXX.smtp.show_requests.myFilter]
});

XXX.smtp.show_requests.myTab.add
({
xtype: 'panel',
bodyStyle: 'background-color: #DFE8F6;',
layout:
{
type: 'vbox',
align: 'stretch',
pack: 'start'
},
border:false,
defaults:
{
border:false
},
items:
[
XXX.smtp.show_requests.myGrid,
XXX.smtp.show_requests.myForm
]
});

XXX.smtp.show_requests.myTab.doLayout();
}
else
{
alert("Error: Could not get tab...");
}
</script>That is the actual tab code that led to this dilema, previously each tab had a unique view in the back-end. However, I was trying to re-use the code for this tab by setting the $is_admin variable to enable a sub-set of features in the form.

I first tried to change how I was referencing the name space from a static value in each tab to one based on the $tab_id that is passed in (see example in 1st post). However, as I stated above, I did run into some problems that way. I've had to change the code to generate a namespace variable in php (based on the tab_id) and then use <?php echo $myNS; ?> in everywhere I reference any object by its namespace.

If there is a simpler, more elegant solution, I'm open to suggestions.

arthurakay
23 Feb 2011, 11:25 AM
First of all, that's a LOT of JavaScript code to have inside a set of script tags on your PHP page. I would highly recommend structuring your code differently, which begins with placing your JS code into a separate JS file.

The next thing I would recommend is to create a custom class extended from Ext.Panel(). Looking at your code...


XXX.smtp.show_requests.myTab.add
({
xtype: 'panel',
bodyStyle: 'background-color: #DFE8F6;',
layout:
{
type: 'vbox',
align: 'stretch',
pack: 'start'
},
border:false,
defaults:
{
border:false
},
items:
[
XXX.smtp.show_requests.myGrid,
XXX.smtp.show_requests.myForm
]
});


...it appears that each of your tabs (or at least several of them) are built in exactly the same way: panels containing a grid and a form.

Rather than having all of that built within a function (or conditional statement...), why not do something like:


customTabObj = Ext.extend(Ext.Panel, {
//your methods/properties defined here
});

//.......

XXX.smtp.show_requests.myTab = Ext.ComponentMgr.get('<?php echo $tab_id; ?>');

if (XXX.smtp.show_requests.myTab) {
XXX.smtp.show_requests.myTab.add( new customTabObj({ configs: xyz }) );
}
else {
alert("Error: Could not get tab...");
}


Because you'd be extending Ext.Panel to create your custom tab class, you could add as many methods as you wanted onto the class to handle the logic you need. It solves the problem you mentioned:



My "point" is to have each tab have its own "scope", so that if I had two tabs, each with objects called myForm and myGrid, that if I called myForm.reset() in a handler in myGrid in tab2, it only affects the myForm in tab2.


Each of these custom tab instances would have its own scope. You can still inject your PHP values, but you'd pass them into the customTabObj() config.