PDA

View Full Version : convert a traditional gridpanel to Ext.extend(Ext.grid.GridPanel



pcrombach
4 Nov 2008, 6:01 AM
Hi, I read a lot of extending Gridpanels. I have build a lot of Gridpanels traditional way. Now I want to convert them to Ext.extend(Ext.grid.GridPanel vor lazy rendering.

I van handle a simple example. But its difficult for me to convert grids with more complex features.

Somebody out there can help me??

Here a sample of a grid I want to convert:

/* GEBRUIKERS */
//
// custom column plugin example
Ext.grid.CheckColumn = function(config){
Ext.apply(this, config);
if(!this.id){
this.id = Ext.id();
}
this.renderer = this.renderer.createDelegate(this);
};

Ext.grid.CheckColumn.prototype ={
init : function(grid){
this.grid = grid;
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},

onMouseDown : function(e, t){
if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
e.stopEvent();
var index = this.grid.getView().findRowIndex(t);
var record = this.grid.store.getAt(index);
record.set(this.dataIndex, !record.data[this.dataIndex]);
}
},

renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
}
};

var checkColumnC = new Ext.grid.CheckColumn(
{header: "C", dataIndex: 'C', width: 20}
);
var checkColumnR = new Ext.grid.CheckColumn(
{header: "R", dataIndex: 'R', width: 20}
);
var checkColumnU = new Ext.grid.CheckColumn(
{header: "U", dataIndex: 'U', width: 20}
);
var checkColumnD = new Ext.grid.CheckColumn(
{header: "D", dataIndex: 'D', width: 20}
);

var gebruikerCM = new Ext.grid.ColumnModel(
[
{header: "Id", dataIndex: 'Id', width: 50, align: 'right', sortable: true},
{header: "Naam", dataIndex: 'Naam', width: 300, sortable: true},
{header: "Profiel", dataIndex: 'Profiel', width: 300, sortable: true},
checkColumnC,
checkColumnR,
checkColumnU,
checkColumnD,
{header: "Onaam", dataIndex: 'Onaam', width: 100, sortable: true},
{header: "Owaarde", dataIndex: 'Owaarde', width: 100, sortable: true},
{header: "Hnaam", dataIndex: 'Hnaam', width: 100, sortable: true},
{header: "Hwaarde", dataIndex: 'Hwaarde', width: 100, sortable: true}
]
);

var gebruikerDS = new Ext.data.Store({
// proxy: new Ext.data.ScriptTagProxy({
// url: 'http://www.fmisondemand.nl//fmisondemand/SysteemBeheer/Ajax/SysteemBeheerGebruikerGridBackendList.php'
proxy: new Ext.data.HttpProxy({
url: '/fmisondemand/SysteemBeheer/Ajax/SysteemBeheerGebruikerGridBackendList.php'
}),

// create reader that reads the Topic records
reader: new Ext.data.JsonReader({
root: 'results',
totalProperty: 'totalCount',
autoLoad: true,
id: 'Id',
method: 'POST'
},
[
{name: 'Id', mapping: 'Id'},
{name: 'Naam', mapping: 'Naam'},
{name: 'Profiel', mapping: 'Profiel'},
{name: 'C', mapping: 'C', type: 'bool'},
{name: 'R', mapping: 'R', type: 'bool'},
{name: 'U', mapping: 'U', type: 'bool'},
{name: 'D', mapping: 'D', type: 'bool'},
{name: 'Onaam', mapping: 'Onaam'},
{name: 'Owaarde', mapping: 'Owaarde'},
{name: 'Hnaam', mapping: 'Hnaam'},
{name: 'Hwaarde', mapping: 'Hwaarde'}
]
),
remoteSort: true
});
gebruikerDS.setDefaultSort('Profiel', 'ASC');
gebruikerDS.baseParams = {ProfielId : profielid};
gebruikerDS.load({params:{start:0, limit:gebruikerPageSize}});

var gebruikerPagingToolbar = new Ext.PagingToolbar({
pageSize: 25,
displayInfo: true,
displayMsg: 'totaal {2} records gevonden. Display nu {0} - {1}',
emptyMsg: "geen gebruikers gevonden.",
store: gebruikerDS
});

var gebruikerMenubar = [{
text:'Toevoegen Gebruiker',
tooltip:'Voeg een nieuwe gebruiker toe',
iconCls:'icon-add',
handler: function(){
// add new user now - action
}
},'-',{
text:'Verwijder gebruiker(s)',
tooltip:'Verwijder de geselecteerde gebruiker(s)',
iconCls:'icon-remove',
handler: function(){
// delete user now - action
}
},'-',{
text: 'Save Changes',
tooltip:'Sla gemarkeerde velden op',
iconCls:'icon-save',
handler: function() {
jsonData = "[";

for(i=0;i<gebruikerDS.getCount();i++) {
record = gebruikerDS.getAt(i);
if(record.data.newRecord || record.dirty) {
jsonData += Ext.util.JSON.encode(record.data) + ",";
}
}

jsonData = jsonData.substring(0,jsonData.length-1) + "]";
var saveChanges = new Ext.data.Connection();
saveChanges.request({
scope: this,
url: '/fmisondemand/SysteemBeheer/Ajax/SysteemBeheerSaveChanges.php',
method: 'POST',
params:{updaters:jsonData},
success: function(response, options){
var messageObj = Ext.util.JSON.decode(response.responseText);
if( messageObj.success ) {
gebruikerDS.commitChanges();
} else {
Ext.MessageBox.show({
title: 'Error',
msg: 'Changes not saved : '+messageObj.msg,
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
}
},
failure: function(form, action){
Ext.MessageBox.show({
title: 'Error',
msg: 'Server not responding',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
}
});
}
}
];

var gebruikerGridPanel = new Ext.grid.GridPanel({
title: 'Overzicht Gebruikers',
border:false,
ds: gebruikerDS,
cm: gebruikerCM,
loadMask: true,
layoutOnTabChange: true,
autoSizeColumns: true,
plugins:[checkColumnC, checkColumnR, checkColumnU, checkColumnD],
sm: gebruikerSM,
tbar: gebruikerMenubar,
bbar: gebruikerPagingToolbar
});

var gebruikerSM = new Ext.grid.CellSelectionModel({singleSelect:true});
gebruikerSM.addListener('cellselect', function(grid, rowIndex, columnIndex, e) {
var record = gebruikerGridPanel.getStore().getAt(rowIndex); // Get the Record for the row
selecteddivisie = record.get('divisie'); // dit is niet de juiste manier(index), zie volgende 2 regels (op naam)
onderdeelds.baseParams = {Divisie:selecteddivisie};
});

Animal
4 Nov 2008, 6:04 AM
You want to subclass GridPanel?

What extra functionality are you adding? I can't read that code.

pcrombach
4 Nov 2008, 6:11 AM
How do I post the code in a box???

Animal
4 Nov 2008, 6:13 AM
use [ code] tags. Without the space in there - I don't want to switch into code mode.

Animal
4 Nov 2008, 6:15 AM
I still don't understand what you want. WHy are you thinking about subclassing GridPanel?

pcrombach
4 Nov 2008, 6:19 AM
You said you dont want to switch into code mode??

I send the code again:



/* GEBRUIKERS */
//
// custom column plugin example
Ext.grid.CheckColumn = function(config){
Ext.apply(this, config);
if(!this.id){
this.id = Ext.id();
}
this.renderer = this.renderer.createDelegate(this);
};

Ext.grid.CheckColumn.prototype ={
init : function(grid){
this.grid = grid;
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},

onMouseDown : function(e, t){
if(t.className && t.className.indexOf('x-grid3-cc-'+this.id) != -1){
e.stopEvent();
var index = this.grid.getView().findRowIndex(t);
var record = this.grid.store.getAt(index);
record.set(this.dataIndex, !record.data[this.dataIndex]);
}
},

renderer : function(v, p, record){
p.css += ' x-grid3-check-col-td';
return '<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>';
}
};

var checkColumnC = new Ext.grid.CheckColumn(
{header: "C", dataIndex: 'C', width: 20}
);
var checkColumnR = new Ext.grid.CheckColumn(
{header: "R", dataIndex: 'R', width: 20}
);
var checkColumnU = new Ext.grid.CheckColumn(
{header: "U", dataIndex: 'U', width: 20}
);
var checkColumnD = new Ext.grid.CheckColumn(
{header: "D", dataIndex: 'D', width: 20}
);

var gebruikerCM = new Ext.grid.ColumnModel(
[
{header: "Id", dataIndex: 'Id', width: 50, align: 'right', sortable: true},
{header: "Naam", dataIndex: 'Naam', width: 300, sortable: true},
{header: "Profiel", dataIndex: 'Profiel', width: 300, sortable: true},
checkColumnC,
checkColumnR,
checkColumnU,
checkColumnD,
{header: "Onaam", dataIndex: 'Onaam', width: 100, sortable: true},
{header: "Owaarde", dataIndex: 'Owaarde', width: 100, sortable: true},
{header: "Hnaam", dataIndex: 'Hnaam', width: 100, sortable: true},
{header: "Hwaarde", dataIndex: 'Hwaarde', width: 100, sortable: true}
]
);

var gebruikerDS = new Ext.data.Store({
// proxy: new Ext.data.ScriptTagProxy({
// url: 'http://www.fmisondemand.nl//fmisondemand/SysteemBeheer/Ajax/SysteemBeheerGebruikerGridBackendList.php'
proxy: new Ext.data.HttpProxy({
url: '/fmisondemand/SysteemBeheer/Ajax/SysteemBeheerGebruikerGridBackendList.php'
}),

// create reader that reads the Topic records
reader: new Ext.data.JsonReader({
root: 'results',
totalProperty: 'totalCount',
autoLoad: true,
id: 'Id',
method: 'POST'
},
[
{name: 'Id', mapping: 'Id'},
{name: 'Naam', mapping: 'Naam'},
{name: 'Profiel', mapping: 'Profiel'},
{name: 'C', mapping: 'C', type: 'bool'},
{name: 'R', mapping: 'R', type: 'bool'},
{name: 'U', mapping: 'U', type: 'bool'},
{name: 'D', mapping: 'D', type: 'bool'},
{name: 'Onaam', mapping: 'Onaam'},
{name: 'Owaarde', mapping: 'Owaarde'},
{name: 'Hnaam', mapping: 'Hnaam'},
{name: 'Hwaarde', mapping: 'Hwaarde'}
]
),
remoteSort: true
});
gebruikerDS.setDefaultSort('Profiel', 'ASC');
gebruikerDS.baseParams = {ProfielId : profielid};
gebruikerDS.load({params:{start:0, limit:gebruikerPageSize}});

var gebruikerPagingToolbar = new Ext.PagingToolbar({
pageSize: 25,
displayInfo: true,
displayMsg: 'totaal {2} records gevonden. Display nu {0} - {1}',
emptyMsg: "geen gebruikers gevonden.",
store: gebruikerDS
});

var gebruikerMenubar = [{
text:'Toevoegen Gebruiker',
tooltip:'Voeg een nieuwe gebruiker toe',
iconCls:'icon-add',
handler: function(){
// add new user now - action
}
},'-',{
text:'Verwijder gebruiker(s)',
tooltip:'Verwijder de geselecteerde gebruiker(s)',
iconCls:'icon-remove',
handler: function(){
// delete user now - action
}
},'-',{
text: 'Save Changes',
tooltip:'Sla gemarkeerde velden op',
iconCls:'icon-save',
handler: function() {
jsonData = "[";

for(i=0;i<gebruikerDS.getCount();i++) {
record = gebruikerDS.getAt(i);
if(record.data.newRecord || record.dirty) {
jsonData += Ext.util.JSON.encode(record.data) + ",";
}
}

jsonData = jsonData.substring(0,jsonData.length-1) + "]";
var saveChanges = new Ext.data.Connection();
saveChanges.request({
scope: this,
url: '/fmisondemand/SysteemBeheer/Ajax/SysteemBeheerSaveChanges.php',
method: 'POST',
params:{updaters:jsonData},
success: function(response, options){
var messageObj = Ext.util.JSON.decode(response.responseText);
if( messageObj.success ) {
gebruikerDS.commitChanges();
} else {
Ext.MessageBox.show({
title: 'Error',
msg: 'Changes not saved : '+messageObj.msg,
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
}
},
failure: function(form, action){
Ext.MessageBox.show({
title: 'Error',
msg: 'Server not responding',
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
}
});
}
}
];

var gebruikerGridPanel = new Ext.grid.GridPanel({
title: 'Overzicht Gebruikers',
border:false,
ds: gebruikerDS,
cm: gebruikerCM,
loadMask: true,
layoutOnTabChange: true,
autoSizeColumns: true,
plugins:[checkColumnC, checkColumnR, checkColumnU, checkColumnD],
sm: gebruikerSM,
tbar: gebruikerMenubar,
bbar: gebruikerPagingToolbar
});

var gebruikerSM = new Ext.grid.CellSelectionModel({singleSelect:true});
gebruikerSM.addListener('cellselect', function(grid, rowIndex, columnIndex, e) {
var record = gebruikerGridPanel.getStore().getAt(rowIndex); // Get the Record for the row
selecteddivisie = record.get('divisie'); // dit is niet de juiste manier(index), zie volgende 2 regels (op naam)
onderdeelds.baseParams = {Divisie:selecteddivisie};
});

pcrombach
4 Nov 2008, 6:21 AM
Hi I want to subclass because I use this grid in more than 1 place (window). As far as I understand you can xtype a subclassed grid and lazy render it where you need it.

Animal
4 Nov 2008, 6:26 AM
"lazy render" doesn't need xtypes. They are not connected.

Just because you add a raw config object containing an xtype property, doesn't mean the class doesn't get instantiated. The COntainer to which you add it has to immediately look up the constructor associated with the xtype, instantiate an object using the constructor, and add it to its items.

If you need the same kind of thing over and over again, write a function which produces it for you.

pcrombach
4 Nov 2008, 6:51 AM
I read this somewhere on the forum initiated by JSAKALOS:

1. Use lazy instantiation (xtype) as much as possible.

The point here is that if you use xtypes the real Ext objects they represent are not even created until they are needed. We've already got used to lazy rendering (render only when needed) but Ext 2 makes a step forward: xtype = if you will need this object then this is how it will be created.


2. Use pre-configured classes.

xtype approach described in point 1. is only possible if you have "pre-configured classes" what are extensions of Ext classes with configuration options applied and/or with added functionality.

3. Implement relations on parent level.

Imagine you have a border layout with grid in west and form in center. Selecting a grid row should display values in form. Now, where to put logic of that? To grid or to form? Neither of them. Grid doesn't know that form exists and form doesn't know that grid exists. The component that is aware of both of them is their common parent (viewport or window).

Therefore, the relations have to be established at the parent level, e.g. window. Code there listens to grid's events and loads form data on selection change and on form submit event it updates grid record.

If I'd put this code to grid the grid cannot exist w/o form, if to form, form cannot exist w/o grid. Got the idea?


4. Keep each class in its own file while developing, concat and minify for production.

Yes, this is Ext approach. I just take all files I include in the header of devel page, concat 'em (in the original order) to one file, minify the result and I have production version.


A final advice: Don't speculate too much about application structure, layout, various controllers, loaders, interfaces, whatever too much. Write good re-usable pre-configured classes instead and put them somehow together in the first place. If they are really good and re-usable you can change your app layout, use a different approach and your classes will still work. Like Lego - if you have blocks you can build a castle in minutes.

So I want to switch my application to pre-configured classes and use them as building blocks.

Is there something wrong to this policy? I always looking for improvement

Animal
4 Nov 2008, 6:55 AM
I have issues with a lot of what is on that site.

It causes a LOT of support problems here.

xtype has nothing to do with lazy instantiation. Add a child to a Container, and the class has to be instantiated. That's how it works.

"preconfigured classes" are an absolute abomination. They cause problems. If you want to create something over and over again, how about a good old fashioned function which just creates whatever?

pcrombach
4 Nov 2008, 7:06 AM
Thank you for quick answering. Although its now more confusing as before I asked my question I understand to not switch massively to "preconfigured classes". Because the last I want is problems. Until now I used the good old fashioned functions. I now think I keep this approach.

jsakalos
28 Jan 2009, 10:37 AM
I have issues with a lot of what is on that site.

It causes a LOT of support problems here.

xtype has nothing to do with lazy instantiation. Add a child to a Container, and the class has to be instantiated. That's how it works.

"preconfigured classes" are an absolute abomination. They cause problems. If you want to create something over and over again, how about a good old fashioned function which just creates whatever?

Although I can understand Animal's abhorrence to the concept of pre-configured classes from the viewpoint of a support team person I think that they themselves do not cause any problems if properly used. Same way "fast cars" do not cause accidents, but "slow drivers driving fast cars" do.

Anyway, I know where potential problems may come from and I'm about to write an article on the matter this weekend. If you use this template file (http://blog.extjs.eu/patterns/file-patters/javascript-extension-file-pattern/) for extending a class no problem except a possible programmer's error can occur.

I run application for ~100 clients with ~150 javascript files and with ~60,000 lines of code fully based on "pre-configured classes" concept w/o any problems.

Animal
28 Jan 2009, 11:10 AM
Yes, that will help, ensuring initialConfig is set.

BernieM
28 Jan 2009, 11:47 AM
I think that the part I struggle with the most on this is determining what should and shouldn't be preconfigured. Things that generally shouldn't be preconfigured include ids, titles, and handlers. It took me a while to figure that out the hard way, though. I can certainly sympathize with Animal's viewpoint here. Factory functions are more intuitive and less error-prone than preconfigured classes. They probably drive OO purists crazy, though.

jsakalos
28 Jan 2009, 12:52 PM
It is not about any kind of "purism" or whatever else of a kind. I just prefer that approach because it provides me modularity that I need for my really big application. Maybe I'm ignorant but I have no idea how could I modularize my 150 extended classes into a factory function(s) and reasonably split it to small manageable files. In my case, it is not about workability, it is about manageability.

Could you please briefly depict how would you cope with an 150 classes and 60,000 lines of code application using (factory) function(s)? I'm ready to learn.

jsakalos
1 Feb 2009, 6:19 PM
I have promised to write an article on pre-configured classes and I'm going to fulfill the promise but I spent almost all my weekend on updating my extension, main site and examples site to reflect the concepts from the upcoming article.

I has been big job as I updated ~30 extensions/plugins, ~20 examples, extension file pattern, main site, examples site, etc.

Stay tuned, I'll publish the article soon.

jsakalos
2 Feb 2009, 4:23 AM
So here it is: http://blog.extjs.eu/know-how/writing-a-big-application-in-ext-part-2/

Solstice
2 Feb 2009, 9:15 AM
Saki,

Great articles, I think you've done a wonderful job in helping people understand some of the basic tenets of OO development.

Animal, I don't fully understand your concern regarding this approach over factory methods. I am new to extJS but have over 13 years of Java experience (and 5 years of C++ before that), so this approach makes a lot of sense to me. As someone with a strong OO background, pre-configured UI components seem like the proper approach.

However, as I said, I am new to extJS (coming here from jQuery) and would very much like to know more of your thoughts and opinions on this matter before I begin incorporating extJS into a very large project of my own.

Thanks,
Sol

mjlecomte
2 Feb 2009, 11:51 AM
...concern regarding this approach over factory methods
The concern stems from many forum posts where people blindly chuck stuff inside initComponent without regard to following some basic concepts and in some cases it just won't work (initialConfig). As a workaround I apply to initialConfig as an attempt to make sure the initial configuration properties get applied to the initialConfig property.

You can read more about it here: http://extjs.com/forum/showthread.php?p=259901#post259901. The pattern for the template at the end of the linked post describes some of the issues to watch out for in more detail, if you follow the links in that post you can find a more rigorous run down beyond that.

jsakalos
2 Feb 2009, 12:12 PM
Has anybody used Factory functions for a really big application? http://extjs.com/forum/showthread.php?p=279920#post279920

jsakalos
9 Feb 2009, 5:36 PM
Nobody has used Factory functions for a big application?

eLuke
9 Feb 2009, 9:11 PM
I'm currently working on what will eventually be a large app, but, unfortunately, I'm not quite there yet. So I apologize for not being able to comment directly, but I thought I'd at least present an idea or two or a perspective that may help the conversation.

I've spent the last few weeks looking at Saki's site and his tutorials and sample code and demos. I want to say "Thank You!" for all of your time and effort that you've given to help the Ext community. It has truly made a difference for me. Moreover, the "Part 2" tutorial explaining how to extend and avoid issues (e.g. initialConfig, etc.) as a follow-up is great work! (I can't imagine how much work this must have been.)

I've also spent a lot of time at the "Learning Center", reading community tutorials that have been spawned from discussions from different threads. They have been extremely helpful as well.

For this discussion, see: Extending_Ext_for_Newbies, at:

http://extjs.com/learn/Tutorial:Extending_Ext_for_Newbies

I've thought a lot about and gone back and forth in my mind regarding how to fundamentally structure and write the "modules" that will make up my application:

1) How will components interact (i.e. where should handlers really live)? (Think Saki's cross component communication example.)

2) Object lifetimes, instantiation/destruction, browser memory consumption, garbage collection, etc.

3) "Pre-configured"/Extended vs. factories vs. a hybrid approach, which is why I thought I'd write this post.

4) Is one of the methods in 3) above more or less composable than the other (i.e. can I mix and match "modules" or "components" together easily, or even between applications)?

I think, in the end, I'll end up with a hybrid approach: some truly, in the OO sense, sub-classes using a constructor/Ext.extend() and some objects that are produced from factories.

In my opinion, one approach doesn't fit all situations, and, perhaps more importantly, I don't necessarily need to "extend" any of the Ext components in a strict sense (i.e. I'm not *adding* any additional functionality to the Ext components; at least not yet). I think this is Animal's basic point.

I feel more comfortable staying away from what I consider to be advanced Ext use and using factories. This allows me to stay away from the pitfalls outlined in Saki's Part 2 extending tutorial. Just to be clear, some of Saki's "pre-configured"/extending examples are definitely the right choice for the user extensions he's so kindly shared and made available to the Ext community. For example, with "IconCombo" (http://extjs.com/learn/Tutorial:Extending_Ext2_Class), he is clearly adding functionality to a base Ext component and "extending" it.

I'll now share the direction I've started to take with my own code using factories.

Right now, virtually all I'm doing anyway is *using* (not *extending*) Ext's components. I'm creating application "modules" like:



// App.grid.Customer.js

// setup namespace
Ext.ns('App.grid');

App.grid.Customer = function() {

return {
// create/return customer grid panel
createCustomerGridPanel: function() {

// click for customer grid
function onClickCustomerGrid(smObj, rowIndex, record) {
var recId = record.get('id');
var recFname = record.get('namefirst');
var recLname = record.get('namelast');
/* ... */
}

var cgp = new Ext.grid.GridPanel({
id: 'customerGridPanel',
region: 'west',
title: 'Customers',
sm: new Ext.grid.RowSelectionModel({
singleSelect:true,
listeners: {
'rowselect': {
fn: onClickCustomerGrid,
scope: this
}
}
}),
/* ... */
});
return cgp;
}

}; // return
}(); // App.grid.Customer
I hope this little sample is enough to give you an idea of where I was thinking of going with this. For example, I could have an "App.form.CustomerOrder.js", etc.

I still have the freedom to include "boilerplate" or default values. For example, I have a "base" namespace for re-useable bits of code that I want in one place for use throughout my app:



// App.base.FormHandlers.js

// setup namespace
Ext.ns('App.base');

// define form handlers
App.base.FormHandlers = {
actioncomplete: function(form, action) {
/* ... */
},
actionfailed: function(form, action) {
/* ... */
}
};
You could also easily add parameters to the creation functions in the factories and pass an Ext config object or anything else you want... then just use Ext.apply() or whatever's appropriate.

Sorry for the long post, I should be quiet now. ;)

I'd be very interested in hearing any thoughts, ideas or feedback.

Thanks Saki, Animal, Condor, mystix, mjlecomte, Santosh and others! I really appreciate everyone's help and ideas!

:D

-e

jsakalos
9 Feb 2009, 11:37 PM
I'm looking at the example of the Customer grid you posted and thinking how would I use that module. I'm thinking of having it in one js file for future maintainability and I'm going to use it. Sure I include the file in the header of the page and then I'll write:


var bl = new Ext.Panel({
layout:'border'
,items:[{
region:'center'
, ...
},
App.grid.Customer.createCustomerGridPanel()
]
});
So far, so good, it works. My layout with west and center appears as expected, if I need the reference to the grid I use Ext.getCmp('customerGridPanel'); and everything's fine.

Now somebody comes and says: "You know, I want this application to be multilingual so get a Slovak translation and put some language selector combo on the page."

OK, so I'll add an argument to the factory function, title, change title from title:'Customers' to title:title. Good? No, wrong. I have also column headers, various messages, paging toolbar, etc. So change argument to lang and implement some logic to get translations from somewhere. Good? Yeah, good, works. Only I need to call it as:


App.grid.Customer.createCustomerGridPanel(lang)
Now, somebody else comes and says: "You know, I want that grid on the right.". You say fine, i'll add another argument "region" and I call it:


App.grid.Customer.createCustomerGridPanel(lang, region)
And somebody else: "I want two such grids to appear on the screen at the same time"; another argument:


App.grid.Customer.createCustomerGridPanel(lang, region, id)
Wait a minute, if I change id, all the rest of the application breaks as my Ext.getCmp('customerGridPanel'); works for one instance but it doesn't for another.

Now imagine, you have 20 such grids. Do all of them have signature "lang, region, id"? Perhaps not, so to avoid endless looking up what signature to use for grid #17, I change the argument to object config:{lang:'sk', region:'west', id:'customerGridPanel'}.

Yes, all the above is possible and would work. Anyway, do you still like the approach?

jsakalos
9 Feb 2009, 11:47 PM
A little point about "Must add a functionality when extending". Take a look at file examples/portal/Portlet.js:


Ext.ux.Portlet = Ext.extend(Ext.Panel, {
anchor: '100%',
frame:true,
collapsible:true,
draggable:true,
cls:'x-portlet'
});
Ext.reg('portlet', Ext.ux.Portlet);
Why Jack has decided to extend?

I would bet that not "added functionality" was the point, but "reusability" or necessity to have one component used many times. Sure, I can only guess, Jack would explain his reasons best. Anyway, I personally, would not extend if I would be 100% sure that I will need that component only once and I would extend if I wasn't that sure.

eLuke
10 Feb 2009, 8:04 PM
Thanks for taking the time to look at the code sample and think it through and test it out.

I see where you're coming from and realize that both designs work, which is why it's not exactly clear to me that one way is "better" than the other in all scenarios. Like most things, I guess there are shades of gray... i.e. it depends on your situation and ultimately there's more than one way to do it.

The answer may be it's largely personal and a matter of style and application design, in addition to whatever design and style the current code base has you're working on.





App.grid.Customer.createCustomerGridPanel(lang, region, id)


Wait a minute, if I change id, all the rest of the application breaks as my Ext.getCmp('customerGridPanel'); works for one instance but it doesn't for another.

Now imagine, you have 20 such grids. Do all of them have signature "lang, region, id"? Perhaps not, so to avoid endless looking up what signature to use for grid #17, I change the argument to object config:{lang:'sk', region:'west', id:'customerGridPanel'}.


Yes, I think your point above is what I was thinking about and trying to decide how I wanted to be able to split up the code and how it might look after "living" awhile (i.e. many revisions/feature additions later).

As for the "lang"/localization addition, I don't think the place for it would be in the "config" parameter to the factory function, at least in a larger app. In a larger app, I think it would be better placed for it to live with the component it's meant to work with (i.e. the "lang" object property + logic would be inside the factory function). Maybe there's a property in the "App" namespace like "App.lang" that's set during application initialization that locale-aware components can check. (Please note: I briefly looked over your localization tutorial since I haven't done this before, so I can't speak from experience about this in particular.)

As for accessing a grid with 20 other instances, yes, you could just call:



App.grid.Customer.createCustomerGridPanel({id:'myGrid-21'})


to assign unique id's or simply save a reference to it in the local function if you need to interact with it further after instantiation.

Per the factory signature concern, you raise a good point. Perhaps a "standard" convention of always having the factories take a config object as the first argument? Although I was thinking of trying really, really hard to avoid letting the parameters to factories grow beyond a single config object.

As for the "region" concern regarding object placement on the page, how is passing "region" in a config object any different from what you would do with an extended component with a registered xtype?

For example:



/* ... */
,items:{id:'myGrid-21', region:'west', xtype:'examplegrid'}
/* ... */




Yes, all the above is possible and would work. Anyway, do you still like the approach?


What's difficult for me is that I'm not raging excited one way or the other. I like the simple syntax and idea of being able to say:



,items:{xtype:'examplegrid'}


But, the more I read code and think about it and try examples, the more it seems like they're more alike than different; the code just ends up being factored in a different way (i.e. extend vs. new, defining/attaching handlers, etc.).

I left out the apply of the config object in my previous post, so, just for discussion, I worked up another example that might be useful. Here it is:



// App.grid.Orders.js

// setup namespace
Ext.ns('App.grid');

App.grid.Orders = function() {

return {

// customer order grid
createCustomerOrderGrid: function(config) {
// click for order grid
function onClickOrderGrid(smObj, rowIndex, record) {
var recQty = record.get('qty');
var recProduct = record.get('product');
var recPrice = record.get('price');
}
var store = new Ext.data.SimpleStore({
fields: [
{name:'qty', type:'int'},
{name:'product', type:'string'},
{name:'price', type:'float'}
]
});
var cog = new Ext.grid.GridPanel(Ext.apply({
// hard-coded config options go here
title: 'Default',
autoExpandColumn: 'product',
store: store,
columns: [
{id:'qty', header:"Qty", sortable:true},
{id:'product', header:"Product Name", sortable:true},
{id:'price', header:"Price", sortable:true, renderer:'usMoney'}
],
sm: new Ext.grid.RowSelectionModel({
singleSelect:true,
listeners: {
'rowselect': {
fn: onClickOrderGrid,
scope: this
}
}
})
}, config));
return cog;
}

}; // return
}(); // App.grid




// App.form.Customer.js

// setup namespace
Ext.ns('App.form');

App.form.Customer = function() {

return {

// customer form
createCustomerForm: function(config) {
var cf = new Ext.FormPanel(Ext.apply({
// hard-coded config options go here
labelWidth: 95,
frame: true,
bodyStyle: 'padding:15px',
items:[{
xtype: 'textfield',
fieldLabel: 'Customer Name',
name: 'name'
},{
xtype: 'textfield',
fieldLabel: 'Email',
name: 'email'
}]
}, config));
return cf;
},

// customer order form
createCustomerOrderForm: function(config) {
var cof = new Ext.FormPanel(Ext.apply({
// hard-coded config options go here
labelWidth: 95,
frame: true,
bodyStyle: 'padding:15px',
items:[{
xtype: 'textfield',
fieldLabel: 'Customer Name',
name: 'name'
},
App.grid.Orders.createCustomerOrderGrid({
title:'New Order'
})
]
}, config));
return cof;
}

}; // return
}(); // App.form


Thanks again for taking the time to comment and offer feedback!

-e

eLuke
10 Feb 2009, 8:16 PM
I would bet that not "added functionality" was the point, but "reusability" or necessity to have one component used many times. Sure, I can only guess, Jack would explain his reasons best. Anyway, I personally, would not extend if I would be 100% sure that I will need that component only once and I would extend if I wasn't that sure.

Yes, I agree, good point. I don't know. What strikes me about this example is its pure simplicity.

What makes a factory approach less friendly for "reusability" or if you intend to use it many times? This is certainly one of my goals as well!

Is there a use case or situation that stands out for you? (Maybe use code in previous post for illustration.)

-e

jsakalos
10 Feb 2009, 10:13 PM
This time I won't go into details, only some general remarks.

Truth for me has always been that, which works.

Now, both styles work if you are looking at the example code of how to create one object. What I would like to see would be a real really big application that would use factory functions. I'd like to see an enthusiastic developer saying "Yes, I already have the big factory-functions-application and I'm happy with that."

We can speculate here till the end of universe if it is a matter of taste, liking, fixed ideas or whatever else to prefer one design to other but we can come to true only in real life. Therefore I had asked, and I'm asking again: Hey, factory functions user, step in here and tell us your story of creating your big application.

I, as extended classes user, can say: Yes, I have the big application (~100,000 lines of code including server side and ~150 javascript files), it works, it is fully localized to 4 languages - other added easily w/o any single line change in the application itself, it sells well, it is easily maintained, debugged and further developed and I'm happy with that.

I'm asking not because "I want to prove that my approach is the only one" but because I want to know.

jsakalos
10 Feb 2009, 10:19 PM
Re localization you mention above: I have tried 2-3 approaches finally landing at what Jack invented for core Ext: override prototypes with translated texts.

Alex84
11 Feb 2009, 1:08 AM
Sorry now for my post. That all will not be clear for me. Probably depends on my less english knowing :">.
In past I wrote "big" applications only in php with a little bit of javascript. When I have there in example a grid or something I need on different pages of the application I made a class of this, and set parameters or not. Since 3 weeks I'm learning Ext.
Now when I won't start a "big" application with Ext and have a grid that should used in several views or windows or what else in this application, and I want set some parameters / configs or not. What should I use? I'm a little bit confused, there are so many examples. Can someone please make a summery of the methods pros and contras with a example link? I know thats lots of desired, but probably it is usefull for people who want to learn Ext properly. And only can learn from the Ext Pros like Saki to support and enrich with the community later with more examples.

Alex84
11 Feb 2009, 2:46 AM
The last hour I experiment a little bit with extending. So I create a sample grid for products.
This grid I registered as xtype. For the grid I can set some configs like customer id for example. This grid I implement in several windows and forms with different config options. Works fine.

Is this handling correct or wrong or what. Don't understand.

ext.productgrid.js


var product_grid = Ext.extend(Ext.grid.EditorGridPanel, {
layout:'fit',
border:false,
stateful:false,
url: 'get_data.php',
customerID:0,
productGroupId:0,
sortField:'products_id',
.....
});

Ext.reg('productgrid', product_grid);window


var testwin = new Ext.Window({
title:'My products',
width:400,
height:400,
plain:true,
layout:'fit',
closable:false,
maximizable:true,
items:{
xtype:'productgrid',
customerID:customer_id,
sortField:'product_description'
}
});

jsakalos
11 Feb 2009, 4:06 AM
Alex, have you seen this tutorials section (http://extjs.com/learn/Tutorials#Application_Design)?

Alex84
11 Feb 2009, 4:19 AM
Alex, have you seen this tutorials section (http://extjs.com/learn/Tutorials#Application_Design)?

no. thank you for the info