Sencha's blog post on the top ten worst practices is worth reading.
Sencha Top 10 Worst Practices
A Summary from a Blog Post
** Please note that all loans are transferred to the rightful owners of the original blog post.
1. Excessive or unnecessary embedding of component structures
Sometimes developers use redundant nesting components, which can lead to unexpected unattractive aesthetics in an application with oddities such as double borders or unexpected layout behavior.
Bad
items: [{ xtype : 'panel', title: 'My Cool Grid', layout: 'fit', items : [{ xtype : 'grid', store : 'MyStore', columns : [{...}] }] }]
WELL
layout: 'fit', items: [{ xtype : 'grid', title: 'My Cool Grid', store : 'MyStore', columns : [{...}] }]
In the above example, the nested panel is redundant because the grid is an extension of the panel. In addition, other elements, such as forms, trees, panel tabs, are an extension of the panel.
2. Memory leaks caused by the failure of unused components.
This is one of the most important rules of all time. In any programming language, it is very important to make sure that components that are no longer used are discarded properly, even in languages such as Java, where the GC does all the cleaning for us, we need to make sure that we do not perform any objects after that how we finish them.
Bad
Ext.define('MyApp.view.MyGrid',{ extend : 'Ext.grid.Panel', columns : [{...}], store: 'MyStore', initComponent : function(){ this.callParent(arguments); this.on({ scope : this, itemcontextmenu : this.onItemContextMenu }); }, onItemContextMenu : function(view,rec,item,index,event){ event.stopEvent(); Ext.create('Ext.menu.Menu',{ items : [{ text : 'Do Something' }] }).showAt(event.getXY()); } });
Each time the user clicks on the grid line, a new context menu is created. Which looks fine, because we see only the last menu.
Supplements (??)
Ext.define('MyApp.view.MyGrid',{ extend : 'Ext.grid.Panel', store : 'MyStore', columns : [{...}], initComponent : function(){ this.menu = this.buildMenu(); this.callParent(arguments); this.on({ scope : this, itemcontextmenu : this.onItemContextMenu }); }, buildMenu : function(){ return Ext.create('Ext.menu.Menu',{ items : [{ text : 'Do Something' }] }); }, onItemContextMenu : function(view,rec,item,index,event){ event.stopEvent(); this.menu.showAt(event.getXY()); } });
This is what is better than the original. It uses the same menu object every time the user right-clicks on the grid view. However, it will retain the menu, even if we kill the grid, which is not what we need.
WELL
Ext.define('MyApp.view.MyGrid',{ extend : 'Ext.grid.Panel', store : 'MyStore', columns : [{...}], initComponent : function(){ this.menu = this.buildMenu(); this.callParent(arguments); this.on({ scope : this, itemcontextmenu : this.onItemContextMenu }); }, buildMenu : function(){ return Ext.create('Ext.menu.Menu',{ items : [{ text : 'Do Something' }] }); }, onDestroy : function(){ this.menu.destroy(); this.callParent(arguments); }, onItemContextMenu : function(view,rec,item,index,event){ event.stopEvent(); this.menu.showAt(event.getXY()); } });
In the above view, when the grid is destroyed, we also destroy the menu.
3. Monster Controllers
Some people call monsters ... Just kidding, but there are several large controllers (not only controllers, but also other components :)) that consist of thousands of lines of code that do all those things that have nothing to do with each other.
It is very important to find a way to split your application into different processors at the beginning of the project, so that you do not have a giant controller that processes all the processes in your application.
Sentence:
Break the application into others
APP FEATURES (in the application for processing orders → Order, delivery, customer search, etc.)
VIEWS (grids, shapes, etc.)
In ExtJS controllers, you can talk to each other.
this.getController('SomeOtherController').runSomeFunction(myParm);
It is also possible to trigger an application-level event that any controller can listen to.
MyApp.getApplication().fireEvent('myevent');
Another controller also listens for an application-level event.
MyApp.getApplication().on({ myevent : doSomething });
4. Poor folder structure for source code
In any application, a good structure is very important because it improves the readability and maintainability of the project. Instead of putting all the controllers in one folder and all the views in another folder, it is better to structure them logically according to their function.
5. Use of global variables
Why is it bad to use global variables? It is sometimes unclear what actual value it has, so this can lead to many confusions, such as
- Name collisions
It's hard to find runtime errors that are hard to debug
What can we do about it? We could define a separate class for them and store them in it.
5.1 First, we create a separate javascript file that contains the variables that need to be changed when using the application.
Runtime.js
5.2 Define a class for storing available global data, in which case the variable myLastCustomer
Ext.define('MyApp.config.Runtime',{ singleton : true, config : { myLastCustomer : 0 // initialize to 0 }, constructor : function(config){ this.initConfig(config); } });
5.3 Then make verification available throughout the application.
Ext.application({ name : 'MyApp', requires : ['MyApp.config.Runtime'], ... });
5.4 Whenever you want to set or set the value of a global variable
5.4.1 To set a value
MyApp.config.setMyLastCustomer(12345);
5.4.2 GET value
MyApp.config.getMyLastCustomer();
6. Using identifiers in components is a bad idea?
Why?
6.1 Because each identifier you define must be unique. In a large application, this can lead to a lot of confusion and problems.
6.2. Easy to let structure handle component naming
// here we define the first save button xtype : 'toolbar', items : [{ text : 'Save Picture', id : 'savebutton' }] // somewhere else in the code we have another component with an id of 'savebutton' xtype : 'toolbar', items : [{ text : 'Save Order', id : 'savebutton' }]
In the above example, there are two buttons with the same name, which leads to name conflicts. To prevent it, use "itemId" instead of id.
xtype : 'toolbar', itemId : 'picturetoolbar', items : [{ text : 'Save Picture', itemId : 'savebutton' }] // somewhere else in the code we have another component with an itemId of 'savebutton' xtype : 'toolbar', itemId: 'ordertoolbar', items : [{ text : 'Save Order', itemId: 'savebutton' }]
Now you can access the above components by their unique names below
var pictureSaveButton = Ext.ComponentQuery.query('#picturetoolbar > #savebutton')[0]; var orderSaveButton = Ext.ComponentQuery.query('#ordertoolbar > #savebutton')[0];
7. Invalid component binding
It is not recommended to use component positioning to obtain a reference to a component. Since someone can change the position of a component without knowing that it is referenced by positioning in another part of the application.
var mySaveButton = myToolbar.items.getAt(2); var myWindow = myToolbar.ownerCt;
How can we get the link? Use the "ComponentQuery" or "up" / "down" methods.
var pictureSaveButton = Ext.ComponentQuery.query('#picturetoolbar > #savebutton')[0]; // Quering against the itemId var mySaveButton = myToolbar.down('#savebutton'); // searching against itemId var myWindow = myToolbar.up('window');
8. Falling to comply with upper / lower case naming conventions
It is important to use good naming conventions as best practice, as this improves the quality of your code and makes it easier to read and understand. It is also important to use meaningful names for all classes, variables, and methods that you define.
Bad
Ext.define('MyApp.view.customerlist',{ // should be capitalized and then camelCase extend : 'Ext.grid.Panel', alias : 'widget.Customerlist', // should be lowercase MyCustomConfig : 'xyz', // should be camelCase initComponent : function(){ Ext.apply(this,{ store : 'Customers', …. }); this.callParent(arguments); } });
WELL
Ext.define('MyApp.view.CustomerList',{ // Use of capitalized and then camelCase extend : 'Ext.grid.Panel', alias : 'widget.customerlist', // use of lowerCase myCustomConfig : 'xyz', // Use of camelCase initComponent : function(){ Ext.apply(this,{ store : 'Customers', …. }); this.callParent(arguments); } });
9. Restrict the component to the layout of the parent components.
Bad
Ext.define('MyApp.view.MyGrid',{ extend : 'Ext.grid.Panel', initComponent : function(){ Ext.apply(this,{ store : 'MyStore', region : 'center', ...... }); this.callParent(arguments); } });
The layout area of the MyGrid panel is set as the center. Therefore, it cannot be reused in another region, such as the "west". Therefore, it is important to define your components so that they can be reused with any problem.
Supplements (??)
Ext.define('MyApp.view.MyGrid',{ extend : 'Ext.grid.Panel', initComponent : function(){ Ext.apply(this,{ store : 'MyStore', ...... }); } }); Ext.create('MyApp.view.MyGrid',{ region : 'center'
There is another way to define a component with default values (in this case, the "region" property) and override the default values when changing is required by default.
GOOD
Ext.define('MyApp.view.MyGrid',{ extend : 'Ext.grid.Panel', region : 'center', // default region initComponent : function(){ Ext.apply(this,{ store : 'MyStore', ...... }); } }); Ext.create('MyApp.view.MyGrid',{ region : 'north', // overridden region height : 400 });
10. Make the code more complex than necessary.
There are many ways to make simple code complex. One of the many ways is to load form data by individually accessing each field in the form.
Bad
// suppose the following fields exist within a form items : [{ fieldLabel : 'User', itemId : 'username' },{ fieldLabel : 'Email', itemId : 'email' },{ fieldLabel : 'Home Address', itemId : 'address' }]; // you could load the values from a record into each form field individually myForm.down('#username').setValue(record.get('UserName')); myForm.down('#email').setValue(record.get('Email')); myForm.down('#address').setValue(record.get('Address'));
WELL
items : [{ fieldLabel : 'User', name : 'UserName' },{ fieldLabel : 'Email', name : 'Email' },{ fieldLabel : 'Home Address', name : 'Address' }]; myForm.loadRecord(record); // use of "loadRecord" to load the entire form at once.