PDA

View Full Version : Grid/Code improvements wanted!



Gr3yh0und
4 Jan 2012, 4:58 AM
Hey guys,

I'm relatively new to ExtJS and just working the last 4 weeks on a larger website with ExtJS embedded. I had some troubles with grids n stuff and have many things "borrowed" from other code.

Maybe someone can have a short look at my grid example (most of them are very similiar) and maybe can give me some tips on how to tune/make it nicer or more secure for later updates? New features or ideas are also welcome!

Thank you!

It basically looks like:



// Enable config
Ext.Loader.setConfig({
enabled: true
});

// Set Path
Ext.Loader.setPath('Ext.ux', 'extjs/examples/ux');

// Include required libraries
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.util.*',
'Ext.state.*',
'Ext.form.*',
'Ext.tab.*',
'Ext.ux.grid.FiltersFeature',
'Ext.ux.CheckColumn',
'Ext.ux.RowExpander',
'Ext.toolbar.Paging',
'Ext.tip.QuickTipManager'
]);

Just prethings...

Now the datamodel (MySQL and MSSQL Backend with php):


// Define our data models
Ext.define('Location', {
extend: 'Ext.data.Model',
fields: ['id', 'idcity', 'short', 'ads', 'name', 'street', 'postal', 'city', 'comment', 'assetlink']
});

// Define Proxy for reading/writing Data to Store
// Location
var proxy_location = new Ext.data.proxy.Ajax({
type: 'ajax',
autoSave: true,
url: 'config/api_mysql.php',
extraParams: {
select: 'id, idcity, short, ads, comment, name, street, postal, city, assetlink',
table: 'location',
order: 'short ASC'
},
actionMethods: {
read: 'POST',
update: 'POST',
create: 'POST',
destroy: 'POST'
},
reader: {
type: 'json',
model: 'Location',
root: 'results',
totalProperty: 'total',
idProperty: 'id'
},
writer: {
type: 'json',
model: 'Location',
root: 'results',
encode: 'true'

},
api: {
read: 'config/api_mysql.php?do=read',
update: 'config/api_mysql.php?do=update',
create: 'config/api_mysql.php?do=create',
destroy: 'config/api_mysql.php?do=destroy'
}
});

// Define data Stores
// Location
var store_location = new Ext.data.Store({
model: 'Location',
pageSize: 21,
proxy: proxy_location
});


Now the form to add a new entry:


// Location Add Form
function locationAddForm() {
Ext.create('widget.window', {
title: 'Neue Lokation erstellen',
closeAction: 'hide',
renderTo: document.body,
width: 300,
height: 600,
minHeight: 400,
layout: 'fit',
resizable: true,
modal: true,
defaults: {
anchor: '100%'
},
items: {
// Formpanel
xtype: 'form',
layout: {
type: 'vbox',
align: 'stretch'
},
border: false,
bodyPadding: 10,
fieldDefaults: {
labelAlign: 'top',
labelWidth: 100,
labelStyle: 'font-weight:bold'
},
defaults: {
margins: '0 0 10 0'
},
items: [
// Formfields
{
xtype: 'textfield',
id: 'short',
fieldLabel: 'Lokationskuerzel im Format: AAA000-0',
allowBlank: false
}, {
xtype: 'textfield',
id: 'name',
fieldLabel: 'Bezeichnung',
allowBlank: false
}, {
xtype: 'combo',
id: 'idcity_add',
fieldLabel: 'Uebergeordneter Standort',
labelWidth: 125,
store: store_city,
queryMode: 'local',
displayField: 'title',
valueField: 'id'
}, {
xtype: 'textfield',
id: 'ads',
fieldLabel: 'AD-Pfad',
allowBlank: true
}, {
xtype: 'fieldcontainer',
fieldLabel: 'Adresse',
labelStyle: 'font-weight:bold;padding:0',
layout: 'hbox',
defaultType: 'textfield',
fieldDefaults: {
labelAlign: 'top'
},
items: [{
flex: 3,
id: 'street',
fieldLabel: 'Strasse',
allowBlank: true
}, {
flex: 1,
id: 'postal',
fieldLabel: 'PLZ',
margins: '0 0 0 5',
allowBlank: true
}, {
flex: 2,
id: 'city',
fieldLabel: 'Ort',
allowBlank: false,
margins: '0 0 0 5',
allowBlank: true
}]
}, {
xtype: 'textareafield',
id: 'comment',
fieldLabel: 'Kommentar',
labelAlign: 'top',
flex: 3,
margins: '0',
allowBlank: true
}, {
xtype: 'textfield',
id: 'assetlink',
fieldLabel: 'AssetDesk Link',
allowBlank: true
}],
buttons: [{
text: 'Abbrechen',
handler: function () {
this.up('form').getForm().reset();
this.up('window').destroy();
}
}, {
text: 'Erstellen',
handler: function () {
if (this.up('form').getForm().isValid()) {
var short = Ext.getCmp('short').getValue();

// Create record and save
var r = Ext.ModelManager.create({
id: '',
idcity: Ext.getCmp('idcity_add').getValue(),
short: Ext.getCmp('short').getValue(),
ads: Ext.getCmp('ads').getValue(),
name: Ext.getCmp('name').getValue(),
street: Ext.getCmp('street').getValue(),
postal: Ext.getCmp('postal').getValue(),
city: Ext.getCmp('city').getValue(),
comment: Ext.getCmp('comment').getValue(),
assetlink: Ext.getCmp('assetlink').getValue()
}, 'Location');
store_location.insert(0, r);
store_location.save();
this.up('form').getForm().reset();
this.up('window').destroy();
Ext.MessageBox.alert('Success!', 'Die neue Lokation ' + short + ' wurde erfolgreich angelegt!');
store_location.load();
}
}
}],
listeners: {
'beforerender': function(){
store_city.load();
}
}
}
}).show();
}


And the second one to edit an entry:


// Location Edit Form
function locationEditForm(editid) {
Ext.widget('window', {
title: 'Edit',
closeAction: 'hide',
width: 300,
height: 600,
minHeight: 400,
layout: 'fit',
resizable: true,
modal: true,
items: {
xtype: 'form',
layout: {
type: 'vbox',
align: 'stretch'
},
border: false,
bodyPadding: 10,
fieldDefaults: {
labelAlign: 'top',
labelWidth: 100,
labelStyle: 'font-weight:bold'
},
defaults: { margins: '0 0 10 0' },
// Textfields
items: [ {
xtype: 'hiddenfield',
id: 'id'
},{
xtype: 'textfield',
id: 'short',
fieldLabel: 'Lokationskuerzel im Format: AAA000-0',
allowBlank: false
},{
xtype: 'textfield',
id: 'name',
fieldLabel: 'Bezeichnung',
allowBlank: false
},{
xtype: 'combo',
id: 'idcity',
fieldLabel: 'Uebergeordneter Standort',
labelWidth: 125,
store: store_city,
queryMode: 'local',
displayField: 'title',
valueField: 'id'
},{
xtype: 'textfield',
id: 'ads',
fieldLabel: 'AD-Pfad',
allowBlank: true
},{
xtype: 'fieldcontainer',
fieldLabel: 'Adresse',
labelStyle: 'font-weight:bold;padding:0',
layout: 'hbox',
defaultType: 'textfield',
fieldDefaults: { labelAlign: 'top' },
items: [{
flex: 3,
id: 'street',
fieldLabel: 'Strasse',
allowBlank: true
}, {
flex: 1,
id: 'postal',
fieldLabel: 'PLZ',
margins: '0 0 0 5',
allowBlank: true
}, {
flex: 2,
id: 'city',
fieldLabel: 'Ort',
allowBlank: false,
margins: '0 0 0 5',
allowBlank: true
}]
},{
xtype: 'textareafield',
id: 'comment',
fieldLabel: 'Kommentar',
labelAlign: 'top',
flex: 3,
margins: '0',
allowBlank: true
},{
xtype: 'textfield',
id: 'assetlink',
fieldLabel: 'AssetDesk Link',
allowBlank: true
}
],
buttons: [{
text: 'Schliessen',
handler: function() {
this.up('form').getForm().reset();
this.up('window').destroy();
}
},{
text: 'Speichern',
handler: function() {
if (this.up('form').getForm().isValid()) {
var record = store_location.getById(editid); // get record
// Edit record
record.set('short', Ext.getCmp('short').getValue());
record.set('idcity', Ext.getCmp('idcity').getValue());
record.set('ads', Ext.getCmp('ads').getValue());
record.set('name', Ext.getCmp('name').getValue());
record.set('street', Ext.getCmp('street').getValue());
record.set('postal', Ext.getCmp('postal').getValue());
record.set('city', Ext.getCmp('city').getValue());
record.set('comment', Ext.getCmp('comment').getValue());
record.set('assetlink', Ext.getCmp('assetlink').getValue());

// Update store and reset form
store_location.sync();
Ext.MessageBox.alert('Success!', Ext.getCmp('short').getValue() + ' wurde erfolgreich geaendert!');
this.up('form').getForm().reset();
this.up('window').destroy();
}
}
}],
listeners: {
// Load Data from Entry
beforerender: {
fn: function() {
store_city.load();
this.getForm().loadRecord(store_location.getById(editid));
}
}
}
}
}).show();
}


Now some function to add image to a row:


// Render icon for grid YES/NO
function rendericon(value) {
if(value == null){
return '<img src="extjs/resources/themes/images/default/dd/drop-no.gif">';
}else{
return '<img src="extjs/resources/themes/images/default/dd/drop-yes.gif">';
}
}


And the coloumns with my action images:


// Define Grid coloumns
// Location
var columns_location = [
{ id: 'location_short', header: 'Index', dataIndex: 'short', flex: 1, sortable: true, editor: {allowBlank: false} },
{ id: 'location_name', header: 'Bezeichnung', dataIndex: 'name', flex: 1, sortable: true, editor: {allowBlank: false} },
{ id: 'location_street', header: 'Strasse', dataIndex: 'street', flex: 2, sortable: true, editor: {allowBlank: false} },
{ id: 'location_postal', header: 'PLZ', dataIndex: 'postal', flex: 1, sortable: true, editor: {allowBlank: false} },
{ id: 'location_city', header: 'Ort', dataIndex: 'city', flex: 1, sortable: true, editor: {allowBlank: false} },
{ id: 'location_ads', header: 'AD', renderer:rendericon, dataIndex: 'ads', textAlign: 'center', sortable: true, width: 30 },
{
xtype: 'actioncolumn',
header: 'Aktion',
width:40,
items: [{
// Edit row
icon: 'extjs/examples/shared/icons/fam/cog_edit.png',
tooltip: 'Editieren',
handler: function(grid, rowIndex, colIndex) {
locationEditForm(grid.getStore().getAt(rowIndex).get('id'));
}
},{
// Delete row
icon: 'extjs/examples/restful/images/delete.png',
tooltip: 'Loeschen',
handler: function(grid, rowIndex, colIndex) {
var sm = grid.getSelectionModel();
store_location.remove(sm.getSelection());
sm.select(0);
store_location.save();
}
}]
},{
xtype: 'actioncolumn',
header: 'Link',
width:40,
items: [{
// Link to AssetDesk
icon: 'resources/images/asset.png',
tooltip: 'AssetDesk',
handler: function(grid, rowIndex, colIndex) {
if(grid.getStore().getAt(rowIndex).get('assetlink')){
window.location = grid.getStore().getAt(rowIndex).get('assetlink')
}else{
Ext.MessageBox.alert('Error!', grid.getStore().getAt(rowIndex).get('short') + ': Kein Link zu AssetDesk hinterlegt!');
}
}
},{
// Link to details page
icon: 'extjs/examples/shared/icons/fam/application_go.png',
tooltip: 'Details',
handler: function(grid, rowIndex, colIndex) {
window.location = "index.php?section=single&id=" + grid.getStore().getAt(rowIndex).get('id') + "&short=" + grid.getStore().getAt(rowIndex).get('short')
}
}]
}
];


Now the main grid in a tab:


// Main function
Ext.onReady(function() {

// Enable tooltips
Ext.tip.QuickTipManager.init();

// Create Tabs with everything
var tabpanel = Ext.create('Ext.tab.Panel', {
renderTo: 'editor-grid',
activeTab: 0,
width: 800,
height: 600,
plain: true,
layout: 'fit',
border: true,
closable: false,
defaults :{
autoScroll: true,
layout:'fit'
},
});

// Create Location Grid panel
tabpanel.add({
xtype: 'grid',
itemID: 'location',
title: 'Lokationen',
store: store_location,
columns: columns_location,
frame: false,
width: 800,
height: 587,
loadMask: true,
dockedItems: [{
// Paging
xtype: 'pagingtoolbar',
store: store_location,
dock: 'bottom',
displayInfo: true,
pageSize: 21
},{
xtype: 'toolbar',
dock: 'top',
items: [{
// Create Button
xtype: 'button',
text: 'Erstellen',
icon: 'extjs/resources/themes/images/default/dd/drop-add.gif',
handler: locationAddForm
}]
}],
listeners: {
'activate': function(){
store_location.load();
},
'edit': function(data){
store_location.save();
}
}
});


Sorry for the german headings :)

Farish
4 Jan 2012, 5:43 AM
You can probably use the same form for add and edit and just make some changes depending on whether it was called for add or edit. You don't need to create a new record. You can directly insert a new record in a store like this:


store.insert(0, {id: value1, name: value2, address: value3});

where id, name, address etc. would be the fields in your model and value1, value2, etc. would be the values you get from your form fields. Similarly, you can just call record.set only once and set all the values at one time.


record.set({id: value1, name: value2, address: value3});

what would be better is that you could use the rowediting plugin in the grid. this would allow you to edit and add entries directly in the grid without using a form!

If you have some specific questions, you may ask. Most people won't read your whole code...

Regards,
Farish

Gr3yh0und
4 Jan 2012, 6:46 AM
Thanks for your fast answer Farish!

I will try to tune to just use one form. The background is, that I actually don't want to display all data in the grid, so often a formular is necessary... :-/

I just struggled during my programming stuff with some implementations for ExtJS 2.x and 3.x... There are a lot of variations out the (i.e. the grid selection which is now "selection" and was "selections" before... Took me one day to figure that out). My idea was just to make it more "compliant" for further operations :)

Farish
4 Jan 2012, 6:56 AM
Ich habe auch vor ca. 6 Monaten mit ExtJS angefangen and es war wirklich schwer!

Gruß,
Farish

skirtle
4 Jan 2012, 3:07 PM
A few thoughts...

Avoid using component ids. Give fields a name instead.

You may be able to simplify things using getFieldValues:

http://docs.sencha.com/ext-js/4-0/#!/api/Ext.form.Basic-method-getFieldValues

e.g.:


record.set(form.getForm().getFieldValues());

You may also find updateRecord to be of interest as you're using loadRecord:

http://docs.sencha.com/ext-js/4-0/#!/api/Ext.form.Basic-method-updateRecord

Even if you choose not to use updateRecord you may find it interesting to read the source code for it.

There's an unnecessary type option here:


var proxy_location = new Ext.data.proxy.Ajax({
type: 'ajax',

This seems a little strange too, why set an anchor when you're using fit layout?


layout: 'fit',
...
defaults: {
anchor: '100%'
},

I'm a bit dubious about the existence of the function locationEditForm. Looks a bit global to me. Can't you just move all the window config options into a subclass then get rid of that function (your handler would just be a one-liner to instantiate the class instead)? I think overall a lot of your code would be benefit from being moved into classes rather than using large configs.