1.How to create your code in MVC Structure.
Angular JS Drawback ::
Its main drawback is a steep learning curve, largely due to its
poor documentation and code examples.
Other Libraries ::
Libraries like jQuery and Prototype are well suited to smaller applications.
Why ExtJS ::
If you’re looking at developing an enterprise application that deals
with graphs, data grids, communicating with a server, and exchanging lot of data, drag and drop behavior etc, Ext JS 4 is a pretty good candidate.
Here’s a list of features in Ext JS 4
• Object-Oriented flavor- JavaScript has never been taken seriously as a programming
language. We never learn JavaScript the way we have learned Java or C# or Ruby. Developers
who come from an OO background find it difficult to shift to the functional style of coding; so JavaScript, as a functional programming language, poses adaptability issues for OO
developers. Ext JS 4 lends an OO flavor to the JavaScript language. So developers who are
accustomed to coding in OO languages will be in tune with the Ext JS 4 API.
• Rich UI controls- Ext JS 4 provides a rich set of UI components, like any other JavaScript
library. The UI controls include different types of form components, and data components
such as grid, tree, and charts. You have to write a few lines of code in JavaScript even to create
a simple label or a textbox. The programmatic approach to using UI components paves the
way for extending and customizing them.
• Support for HTML 5- HTML 5 provides a set of features like a new set of UI tags, multimedia
capabilities without depending on third party plugins, data storage facilities, web sockets, web
workers, Canvas API for drawing, GeoLocation, and working with history. The Ext JS 4 API
supports HTML 5 tags, working with local storage and session storage, drawing, etc.
• MVC architecture- Modularity has always been an issue in JavaScript libraries. Maintenance is a nightmare in web applications developed using JavaScript, no matter what framework we use. Incorporating new behavior into existing code is a tedious task. Very few libraries in
JavaScript take care of modularity. Ext JS 4 stands apart in this respect. One of the reasons for Ext JS 4’s popularity is the support that it offers for implementing MVC. The complete code can be organized into folders and files following the MVC architecture. Making changes and
testing becomes easier because of this.
• Theming and Styling- Creating stylesheets by writing vanilla CSS code for an entire application can be very frustrating: it’s an uphill task. Ext JS 4 gives us themes that can be used in applications. You can modify those themes to suit your needs. But we don’t have to write CSS code to do that. Ext JS 4 relies upon SASS (Syntactically Awesome StyleSheets) scripts
for styling. The style sheets for the UI controls are available as SASS files which we can play with. The SASS scripts are then compiled to CSS files using a Ruby script called Compass. This makes styling our applications much easier.
• Documentation- Sencha maintains very good API documentation for all the versions. The documentation is very well organized and includes nice reference material, something I have missed while working with other JavaScript libraries. It has good code examples that illustrate the usage of many features.
this.callParent :: You can call a base class method from the derived class method in Ext JS 4 by writing this.callParent(arguments);
The arguments passed to the Manager’s work function can be supplied to the Employee’s work function by using
the arguments keyword in JavaScript as shown below.
this.callParent(arguments);
You can extend a class and override the constructor as shown below. You can invoke the base class
constructor using this.callParent method.
Ext.define("Manager",{
extend : "Employee",
constructor : function(cfg){
this.callParent(arguments);
}
});
Inheriting a UI component class and overriding the constructor
is not a recommended practice
Create a button field in Extjs
If you have to display a Hello World button, we’ll create an object
of the Ext.Button class and render it to the document body as
shown below.
Ext.onReady(function () {
Ext.create("Ext.Button",{
text : "Hello World",
renderTo : Ext.getBody()
});
});
One noteworthy aspect of Ext JS 4 code is its object-oriented approach. All the UI components are available as classes, and we just need to create instances of the classes and render them.
Classes and Objects
You can define a new class in Ext JS 4 using the Ext.define method. You pass in the class name and the object where
you define the attributes and behavior of the class as arguments to the Ext.define method.
Ext.define("Book",{});
If you have worked with Ext JS 3 or the earlier versions, you will notice that the Ext.define method incorporates the functionalities of using Ext.reg, Ext.ns, and Ext.extend methods.
Ext.create("Book");
You can specify a fully qualified class name with a root namespace and package names included as shown below.
Ext.define("DuraSoft.tech.extjs4.Book",{});
In traditional OO languages the new keyword is used to create objects. You can use new to create objects in Ext JS 4 also.
var book1 = new DuraSoft.tech.extjs4.Book();
The Ext.create("classname") method dynamically loads all the JavaScript files that the classname is dependent on before creating an instance, whereas this is not possible when you use the new keyword.
Constructor ::
Constructor is the first function that’s called when an object is created. You can define constructors in our classes using the special property constructor. The constructor property is wired to a function that gets invoked when
an object is created using Ext.create.
Ext.define("DuraSoft.tech.extjs4.Book",{
constructor : function(){
console.log("Book created");
}
});
Config
Ext JS 4 provides a config section for every class where you can list the attributes of the class with default values.
The object can be created by initializing the attributes in which you are interested.
Ext.define("DuraSoft.tech.extjs4.Book",{
config : {
title : "",
price : -1,
authors: []
},
constructor : function(cfg){
this.initConfig(cfg);
}
});
The config section is initialized in the constructor by calling the initConfig method
Loading Dependencies ::
Ext JS 4 introduces the concept of dynamic loading of the dependent JavaScript files. You can specify the classes that your code is dependent on and the appropriate class files are loaded dynamically.
Ext.Loader.require("Ext.button.Button");
(or)
Ext.require("Ext.button.Button");
Ext.require loads the Button.js file and recursively the Button’s dependent files as well.
There’s another way of specifying the loading information by using the requires property in a class, as shown below.
Ext.define("MyPanel",{
requires : ["Ext.button.Button"]
});
DoLayout ::
doLayout method triggers the container to recalculate the layout and refresh itself.
Down ::
This method, similar to the up method in Component class, is used to navigate to the descendants of the container
that matches the expression passed as an argument. For example if you have a container, say container1, that has a
button calling container1.down("button") walks down the container and returns the button component that’s a
child or grandchild or any descendant.
Types of Columns ::
Ext.grid.column.Action actioncolumn Represents a column that renders a collection of icons each with a click handler. Usually used to perform
operations like add, edit, delete, etc.
Ext.grid.column.Date datecolumn :: Used to render a date formatted column
Ext.grid.column.Number numbercolumn :: Used to render a number formatted column
Ext.grid.column.CheckColumn checkcolumn Represents a checkbox column
Ext.grid.column.Boolean booleancolumn Used to display a boolean value formatted column
Ext.grid.column.TemplateColumn templatecolumn Used to specify an XTemplate instance to format the data
Ext.grid.RowNumberer rownumberer Render row numbers
Ext.create("Ext.grid.Panel", {
store: movieStore,
height : 150,
title : "Tom Hanks collection",
columns: [
{xtype : "rownumberer"},
{
text: 'Movie',
dataIndex: 'title',
},
{
text: 'Rent',
dataIndex: 'rent',
xtype : "numbercolumn",
format : "0.00"
},
{
text: 'Buy',
dataIndex: 'buy',
xtype : "numbercolumn",
format : "0.00"
},
{
text : "Blue-ray",
dataIndex : "bluerayFormat",
xtype : 'booleancolumn',
trueText : "Available",
falseText : "Not available"
},
{
text: 'DVD release date',
dataIndex: 'dvdReleaseDate',
xtype : "datecolumn",
format : "M d, Y"
},
{
text : "Wiki",
xtype : "templatecolumn",
tpl : "<a href='{wikipedia}' target='_blank'>Read</a>"
},
{
text: 'Add',
xtype : "actioncolumn",
items : [
{
tooltip:"Edit",
icon : "edit.jpg",
handler : function(grid,rowNum,colNum){
//Edit logic
}
},
{
tooltip:"Delete",
icon : "delete.jpg",
handler : function(grid,rowNum,colNum){
//Delete logic
}
}
},
]
});
Grid Plugins ::
The base class for any plugin is Ext.AbstractPlugin. The grid panel has behaviors like editing, grouping, drag and drop, etc.
Ext.grid.plugin.Editing Used for editing the row and cells of the grid
Ext.grid.plugin.RowExpander Used for creating expandable rows
Ext.grid.plugin.BufferedRenderer Used to render large number of records in a grid
Ext.grid.plugin.DragDrop Implements the drag and drop functionality in a grid
Ext.grid.feature.Feature This plugin acts as a base class for a number of features like grouping, summary, etc.
Each plugin has an alias name known as
ptype similar to
xtype. You can configure the plugin using its ptype or by using Ext.create() method.
The rowediting and cellediting plugins provide four events beforeedit, edit, canceledit, and validateedit.
The validateedit event is called before updating the underlying record in the store
Ext.grid.plugin.Editing ::
This plugin provides the grid editing behavior. This plugin class has two subclasses, Ext.grid.plugin.CellEditing and
Ext.grid.plugin.RowEditing, to either edit the cell or the row. The working of this plugin depends on a configuration attribute of the grid class called selType. The selType attribute denotes the selection model. You can select a cell or a row
in a grid. The value of selType can either be cellModel or rowModel.
plugins : [
Ext.create("Ext.grid.plugin.RowExpander",{})
]
You can use the ptype as shown below.
plugins : [
{
ptype : "rowexpander"
}
]
CellEditing ::
You can edit the cell of the grid using this plugin. The ptype or the alias name of this plugin class is cellediting.
This plugin when added to the grid with the selType as cellModel, converts the cell to editable format when the cell is double-clicked.
Ext.create("Ext.grid.Panel", {
store: movieStore,
height : 150,
title : "Tom Hanks collection",
selType : "cellmodel",
plugins : [
{
ptype : "cellediting",
clicksToEdit : 2
}
]
Ext.grid.plugin.RowExpander ::
This plugin class provides the row in the grid to have contents that can be collapsed and expanded. It uses the RowBody feature
Ext.create("Ext.grid.Panel", {
store: movieStore,
height : 300,
title : "Tom Hanks collection",
plugins: [
{
ptype: 'rowexpander',
rowBodyTpl : [
'<p>{description}</p>'
]
}
]
Ext.grid.plugin.BufferedRenderer ::
If you have a grid that displays a large number of records, scrolling the grid vertically may have a visual impact. When you try to scroll down a grid, the current viewable set of rows in the grid have to be replaced with a new set of data has to be shown in the grid. In the earlier versions of Ext JS 4, this feature was implemented by re-creating all the rows every time you scroll. In other words, the grid was being redrawn everytime. This may lead to a delayed response or a visual blur or sometimes the grid may even become unresponsive for a few seconds or more.
The latest version of Ext JS, 4.2 solves this problem by not replacing or re-rendering rows when you scroll. It just adds or removes the rows to the grid without re-rendering the entire grid panel. What they do is create a set of rows before and after the current viewable set of rows and store them in a buffer. When you scroll down the buffered set of rows is shown in the grid.
You can configure the grid with the BufferedRenderer plugin to configure the buffered rendering of the rows in the grid. The buffered rendering is also popularly known as infinite scrolling.
The Plugin has attributes like trailingBufferZone and leadingBufferZone to specify the number of rows that have to be buffered before and after the current viewable set of rows.
It also provides an attribute numFromEdge that indicates the number of rows from the edge of the table. Say the numFromEdge is 5, and if the viewable area is scrolled till you have 5 rows from the edge of the table, then the grid is
appended with newer set of rows.
You can configure the plugin with the grid as shown below.
plugins : [
{
ptype :"bufferedrenderer",
trailingBufferZone : 10,
leadingBufferZone : 20,
numFromEdge : 7
}
]
Grid Features ::
Ext.grid.feature.Feature class is a grid plugin that provides functionalities like grouping, summary etc.,
Ext.grid.feature.RowBody: Used for adding additional contents to a grid row
Ext.grid.feature.Grouping: Used to create grouped records
Ext.grid.feature.Summary: Used for displaying summary data in a grid
Ext.grid.feature.GroupingSummary: Used to display summary data for each grouped set of records
Ext.grid.feature.RowBody
RowBody feature is used add additional contents to a grid row. In other words, it’s used to implement a nested grid. It generates an additional <tr> element. The ftype of RowBody class is rowbody. The RowBody feature is pretty similar to
the RowExpander plugin that we discussed earlier, the only difference being the lack of the expand/collapse functionality provided by RowExpander. You have to configure the getAdditionalData() method of the feature to return the contents of
the additional row. Here’s how you create a grid with rowbody feature.
Ext.create("Ext.grid.Panel", {
store: movieStore,
height : 350,
title : "Tom Hanks collection",
features: [
{
ftype: 'rowbody',
getAdditionalData : function(data){
return {
rowBody : "<p>" + data.description + "</p>",
rowBodyColspan : 3
};
}
}],
columns: [
{
text: 'Movie',
dataIndex: 'title',
},
{
text: 'Rent',
dataIndex: 'rent',
},
{
text: 'Buy',
dataIndex: 'buy',
}
]
});
Ext.grid.feature.Summary ::
The summary feature is a simple plugin used to display summary information of the records for each column. The summary for each column is specified using two properties, summaryType and summaryRenderer. The summaryType is a
mathematical calculation attribute. You can set the summaryType to be one of the values in this list: sum, count, average, max, min. The summaryRenderer is a function that formats the display information. Figure 6-15 shows the grid with a
summary feature.
Ext.create("Ext.grid.Panel", {
store: movieStore,
height: 350,
title: "Tom Hanks collection",
features: [
{
ftype: 'summary'
}],
columns: [
{
text: 'Movie',
dataIndex: 'title',
summaryType: "count",
summaryRenderer: function (value) {
return "<b>Movie count: " + value + "</b>";
}
},
{
text: 'Rent',
dataIndex: 'rent',
summaryType: "average",
summaryRenderer: function (value) {
return "<b>Average rent: $" + Ext.Number.toFixed(value, 2) + "</b>";
}
},
{
text: 'Buy',
dataIndex: 'buy',
summaryType: "sum",
summaryRenderer: function (value) {
return "<b>Total : $" + Ext.Number.toFixed(value, 2) + "</b>";
}
}
]
});
Ext.grid.feature.Grouping ::
Ext.grid.feature.Grouping plugin is used to group rows in a grid based on the group field that you configure in the data store. Let’s modify our movie store to add a grouping field as shown below.
var movieStore = Ext.create("Ext.data.Store", {
fields: ["title", "rent", "buy", "rating"],
groupField : "rating",
data: [
{ title: "Forrest Gump", rent: 2.99,buy:6.99,rating:"PG-13"},
{ title: "Cast Away", rent: 3.99,buy:13.46,rating:"PG-13"},
{ title: "Apollo 13", rent: 3.99, buy: 7.99, rating: "PG" },
{ title: "The Green Mile", rent: 1.99, buy: 9.99, rating: "R" },
{ title: "Sleepless in Seattle", rent: 1.99, buy: 11.97, rating: "PG" },
{ title: "Toy Story 3", rent: 1.99, buy: 14.99, rating: "G" },
]
});
We’ve introduced a rating field that’ll serve as the groupField. Let’s display the grid where records are grouped
based on their rating. Here’s the grid where we’ve used the grouping feature.
Ext.create("Ext.grid.Panel", {
store: movieStore,
height: 350,
title: "Tom Hanks collection",
features: [
{
ftype: 'grouping',
groupHeaderTpl : "Rating: {name}",
collapsible : false,
showSummaryRow : true
}
],
columns: [
{
text: 'Movie',
dataIndex: 'title'
},
{
text: 'Rent',
dataIndex: 'rent'
},
{
text: 'Buy',
dataIndex: 'buy'
}
],
renderTo: Ext.getBody()
});
Ext.grid.feature.GroupingSummary ::
Ext.grid.feature.GroupingSummary plugin is used to add summary information for the group rows. It’s a combination of the Grouping and Summary features. The code snippet for the grid panel with grouping summary feature is given below.
Ext.create("Ext.grid.Panel", {
store: movieStore,
height: 350,
title: "Tom Hanks collection",
features: [
{
ftype: 'groupingsummary',
groupHeaderTpl: "Rating: {name}",
showSummaryRow : true
}],
columns: [
{
text: 'Movie',
dataIndex: 'title',
summaryType: "count",
summaryRenderer: function (value) {
return "<b>Movie count: " + value + "</b>";
}
},
{
text: 'Rent',
dataIndex: 'rent',
summaryType: "average",
summaryRenderer: function (value) {
return "<b>Average rent: $" + Ext.Number.toFixed(value, 2) + "</b>";
}
},
{
text: 'Buy',
dataIndex: 'buy',
summaryType: "sum",
summaryRenderer: function (value) {
return "<b>Total : $" + Ext.Number.toFixed(value, 2) + "</b>";
}
}
],
});
1.What is Mixins?
Mixins help you to mix the behavior of different classes into your class. Your class can have the functionalities of any number of classes mixed together. It’s somewhat similar to interfaces in Java where a class can implement any number
of interfaces.
24
www.it-ebooks.infoChapter 3 ■ Understanding the Ext JS 4 API
Let’s create two classes, Aquatic and Terrestrial, with swim and walk functions, respectively.
Ext.define("Aquatic",{
swim : function(){
console.log("Swimming");
}
});
Ext.define("Terrestrial",{
walk : function(){
console.log("Walking");
}
});
We’ll create a class Reptile that can walk as well as swim. The Reptile class is created by mixing Aquatic and
Terrestrial together.
Ext.define("Reptile",{
mixins : ["Aquatic","Terrestrial"]
});
A Reptile instance can invoke the walk and swim functions.
var reptile = Ext.create("Reptile");
reptile.swim();
reptile.walk();
2. How to hide particular column cell in grid panel
grid.on('beforeedit', function(e) {
if (e.record.get('phone') == "555-111-1224")
grid.getPlugin('rowEditing').editor.form.findField('name').disable();
else
grid.getPlugin('rowEditing').editor.form.findField('name').enable();
});
3. How to create multiple instance in a store?
Ext.define('MyGrid', {
define : 'Ext.grid.Panel',
alias : 'widget.mygrid',
title : 'My Grid',
initComponent : function() {
var me = this;
Ext.apply(me, {
store : me.buildStore()
});
me.callParent(arguments);
},
buildStore : function() {
return Ext.create('MyStore', {
//...
});
}
});
4. How to create listbox or how to list items one by one in combox box without any external plugin in extjs?
5.Difference between extjs 4 and 5?
6.Store used in tree panel
7.Event bubbling and event propagation
8.Bind in javascript
9.Event loop
10.ptype in ExtJS
11.initcomponent.
12.Absolute and anchor layout difference
13.
doLayout(
) in fieldset?
Manually force this container's layout to be recalculated. The framework uses this internally to refresh layouts
form most cases.
Types of Store ::
ArrayStore This class is used if the data is a simple array collection.
JsonStore Used when the data is in JSON format
JsonPStore Used to hold JSON formatted data loaded from different domains. This class uses the JsonPProxy class.
XmlStore You’ve an XML formatted data that needs to be loaded into a Store and XmlStore comes in here.
TreeStore Mainly used in tree control, this class is used to load hierarchical data.
DirectStore Ext Direct is a specification that paves way for communication of Ext JS 4 clients with the server
platforms. The DirectStore class uses a Direct Proxy class to talk to
the server and exchange JSON data.
InitComponent ::
This method is invoked by the constructor. It is used to initialize data, set up configurations, and attach event handlers.
Another interesting point is that
initComponent is the
place where child components (for container), stores, view, templates,
etc., are created. So, before calling the superclass
initComponent method
The
initComponent template method is an important initialization step for a
Component. It is intended to be implemented by each subclass of Ext.Component to provide any needed constructor logic. The
initComponent method of the class being created is called first, with each
initComponent method up the hierarchy to Ext.Component being called thereafter. This makes it easy to
implement and, if needed, override the constructor logic of the Component at
any step in the hierarchy.
The initComponent method must contain a call to callParent in order to ensure that the
parent class' initComponent method is also called.
The constructor() is the object/class before create method. And initComponent() is the component before show method.
Failure property form submit
The failure callback, on the other hand, can be extended to look for specific reasons why the failure occurred (for example, there was an internal server error, the form did not pass client-side validation, and so on). This is done by looking at the failureType property of the action parameter.
Ext.form.action.Action has four failureType static properties:
CLIENT_INVALID ,
SERVER_INVALID ,
CONNECT_FAILURE , and LOAD_FAILURE , which can be used to compare
with what has been returned by the server.
failure: function(form, action){
if (action.failureType === Ext.form.action.Action.CLIENT_INVALID) {
Ext.Msg.alert('CLIENT_INVALID', 'Something has been missed.
Please Check and try again.');
}
if (action.failureType === Ext.form.action.Action.CONNECT_FAILURE) {
Ext.Msg.alert('CONNECT_FAILURE', 'Status: ' + action.response.status
+ ': ' + action.response.statusText);
}
if (action.failureType === Ext.form.action.Action.SERVER_INVALID) {
Ext.Msg.alert('SERVER_INVALID', action.result.message);
}
if(action.failureType == Ext.form.action.Action.LOAD_FAILURE){
Ext.Msg.alert('LOAD_FAILURE', action.result.message);
}
}
An alternative to CLIENT_INVALID
The isValid method in Ext.form.Basic is an alternative method for handling
client-side validation before the form is submitted. isValid will return true when
client-side validation passes:
handler: function(){
if (formPanel.getForm().isValid()) {
formPanel.getForm().submit({
url: 'submit.php'
});
}
}
Defining a Controller
Controllers are the glue that binds an application
together. All they really do is listen for events (usually from views)
and take some actions. Continuing our Account Manager application, lets
create a controller. Create a file called app/controller/Users.js and add the following code:
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
init: function() {
console.log('Initialized Users! This happens before the Application launch function is called');
}
});
Now lets add our newly created Users controller to the application config in app.js:
Ext.application({
...
controllers: [
'Users'
],
...
});
When we load our application by visiting index.html inside a browser, the Users controller is automatically loaded (because we specified it in the Application definition above), and its init function is called just before the Application's launch function.
The
init function is a great place to
set up how your controller interacts with the view, and is usually used
in conjunction with another Controller function -
control. The
control function makes it easy to listen to events on your view classes and take some action with a handler function. Let's update our
Users controller to tell us when the panel is rendered:
Ext.define('AM.controller.Users', {
extend: 'Ext.app.Controller',
init: function() {
this.control({
'viewport > panel': {
render: this.onPanelRendered
}
});
},
onPanelRendered: function() {
console.log('The panel was rendered');
}
});
DoLayout and DoComponentLayout ::
The two are actually quite different. doLayout is a container method used to set the sizes of child components within the container.
doComponentLayout is a component method used to size the elements (typically) that make up that component.
RowEditing ::
var rowEditing = Ext.create('Ext.grid.plugin.RowEditing', {
clicksToMoveEditor: 1,
autoCancel: false
});
rowEditing.cancelEdit(); -> Cancel Previous edit
Define our data model ::
Ext.define('Employee', {
extend: 'Ext.data.Model',
fields: [
'name',
'email',
{ name: 'start', type: 'date', dateFormat: 'n/j/Y' },
{ name: 'salary', type: 'float' },
{ name: 'active', type: 'bool' }
]
});
Create a model instance ::
var r = Ext.create('Employee', {
name: 'New Guy',
email: 'new@sencha-test.com',
start: new Date(),
salary: 50000,
active: true
});
store.insert(0, r); ->Insert Record at first position in a store
rowEditing.startEdit(0, 0); ->Editing shows at first cell and first row in a grid
Panel
1.SAAS vs CSS
2.Create single instance of store
3.Apply Template in view
4.Find particular column cell and change the value in grid panel
{
header: 'Light',
dataIndex: 'light',
width: 130,
editor: {
xtype: 'combobox',
typeAhead: true,
triggerAction: 'all',
selectOnTab: true,
store: [
['true','true'],
['false','false']
],
listeners: {
select : function(field, eOpts){
if(field.getValue()=="false"){
alert(field.getValue());
var rec = gridpanel.getStore().findRecord("price","7.16");
rec.set("price", "999");
}
}
},
lazyRender: true,
listClass: 'x-combo-list-small'
}
}
5.APP.js and Application.js
6.Debug error in extjs
Use Firebug, ext-alldebug.js and Ext.error.handle function
7.Load and LoadRecord
Load : Load data in the store via configured proxy
LoadRecord :: Load data in the store from the array of model instances
LoadData(data, boolean):: Load the array of data straight in the store
True:: To append new records from the existing records.
False :: To replace existing records with new one
8.Load data in a combobox using a single call
9.Viewport
10.Handle browser compatibility issue.
11.ExtJS Best Practices
Maintain an organized project structure. Placing
every view under “views” in a directory is likely to cause headaches
somewhere down the line, especially if a developer unfamiliar with the
project needs to find the source of a particular view.
Group views in sub-folders based on relevance and function. As the
project grows and the number of views increases, more tiers should be
added to your folder structure. Adding another folder in the hierarchy
will have very little impact in the execution of the application, but
has the potential to save some frustration.
Your source code should conform to a coding standard.
A set standard will help fight against spaghetti code, promote
reusability, and facilitate learning. When in doubt, the best code
structure you can conform to is the ExtJS source code itself.
Ext.override
Ext.override
is a function that allows you to override classes and add or change any
functionality you desire. It’s important to understand that if you
override a base class, the functionality will affect every instance of
that class and any classes that inherit from it. There’s a good chance
you will introduce a few defects in the process. Also, if you decide to
upgrade to a newer version of ExtJS, there’s a chance your override will
no longer work as intended.
There will be times when it is necessary to override a class. In
these cases, be sure to use best practices when applying the overrides.
Do not just place all of your overrides in one file and let it grow over
time. Break them apart into their own succinct files and make plenty of
comments to describe the reasoning behind and outcome of your changes.
It will save time if another developer has to revisit that override
after a long period of time.
Excessive Layouts
Layouts can be the bane of an ExtJS application’s existence. They can
be triggered by adding or removing new components, changing a
component’s content or size, updating a record displayed in a grid with a
custom renderer, and much more.
Any time you have a code block that is making these types of changes be sure to surround it with:
suspendLayouts();
#code causing layouts to occur
resumeLayouts(true);
Or if you are CRUDing records in a store that is causing a grid to perform multiple layouts, consider using:
gridStore.suspendEvents();
#CRUD operations
gridStore.resumeEvents();
Large Grid Datasets
BufferedRenderer
is a great plugin to use with grid panels when dealing with large
datasets. Essentially this acts as an infinite scrolling solution and
rows are only rendered onto the screen when they need to be, rather than
all at once.
var grid = Ext.create('Ext.grid.Panel', {
//..
autoLoad: true,
plugins: {
ptype: 'bufferedrenderer',
trailingBufferZone: 10,
leadingBufferZone: 20,
numFromEdge: 6
},
//..
});
The config options to note are:
- trailingBufferZone – the number of rows rendered above the table
- leadingBufferZone – the number of rows rendered below the table
- numFromEdge – the number of
rows from the end of the leading or trailing buffer zones that will
cause more rows to be rendered into the table
Lazy Instantiation
Attempt to lazily instantiate your items array wherever possible. A
primary example would be the hidden tabs of a tabpanel. If the parent
container never gets rendered, the child items aren’t instantiated. The
plugin
here will allow you to accomplish this.
{
xtype: 'tabpanel',
items: [{
title: 'T1',
plugins: {
ptype: 'lazyitems',
items: [...]
}
}, {
title: 'T2',
plugins: {
ptype:'lazyitems',
items: [...]
}
}, {...}]
}
Nesting
Overnesting occurs if a component has a single item in its list,
which in turn has a single item of its own that defines the rest of the
components in the view. The item in the middle should be omitted
completely, if the functionality remains the same. Doing so keeps the
application structure simple to help reduce the DOM burden and speed up
your application.
This
bookmarklet
helps to automate the search and point you toward problem areas where
your application might have some unneeded nesting. A simple way to run
this is to copy and paste the code from Gist into your browser’s
developer console while the application is running. You will notice
areas of the app highlight in red and any problem areas will be logged
in the console.
Moving Forward
Sencha continues to improve the features and functionality of ExtJS,
with a focus on performance. Upcoming plugins such as
Ext.grid.plugin.CellUpdating will help allow developers to sidestep some
of the performance issues that can occur today. However, it will always
be up to the developer to use ExtJS the right way in order to get the
most out of the framework. Being proactive and learning about the
potential pitfalls early on will help clear the road of bugs and open
the way to progress.
1. Excessive or unnecessary nesting of component structures
One of the most common mistakes developers make is nesting components
for no reason. Doing this hurts performance and can also cause
unappealing aesthetics in the app with oddities such as double borders
or unexpected layout behavior. In example 1A below, we have a panel that
contains a single grid. In this case, the panel is unnecessary. As
shown in example 1B, the extra panel can be eliminated. Remember that
forms, trees, tab panels and grids all extend from Panel, so you should
especially watch for unnecessary nesting conditions whenever using these
components.
items: [{
xtype : 'panel',
title: ‘My Cool Grid’,
layout: ‘fit’,
items : [{
xtype : 'grid',
store : 'MyStore',
columns : [{...}]
}]
}]
Example 1A. BAD: The ‘panel’ is unnecessary.
layout: ‘fit’,
items: [{
xtype : 'grid',
title: ‘My Cool Grid’,
store : 'MyStore',
columns : [{...}]
}]
Example 1B. GOOD: The grid is already a panel so just use any panel properties directly on the grid.
2. Memory leaks caused by failure to cleanup unused components.
Many developers wonder why their apps get slower and slower the
longer they are used. Failure to cleanup unused components as a user
navigates throughout an app is one of the biggest reasons. In example 2A
below, each time the user right-clicks on a grid row, a new context
menu is created. If the user keeps this app open and right-clicks the
row hundreds of times, they will wind up with hundreds of context menus
that will never be destroyed. To the developer and user, the app “looks”
visually correct because only the last context menu created is seen on
the page. The rest are hidden away. As new menus are created without the
old ones being cleaned up, the memory utilization for the app will keep
increasing. This will eventually result in slower operation or a
browser crash.
Example 2B is better because the context menu is created once when
the grid is initialized and is simply reused each time the user
right-clicks a row. However, if the grid is destroyed, the context menu
will still exist even though it is no longer needed. The best scenario
is example 2C where the context menu is destroyed when the grid is
destroyed.
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());
}
});
Example 2A. BAD: A menu will be created on every right-click and won’t ever be destroyed.
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());
}
});
Example 2B. BETTER: Menu will be created when the grid is created and be reused each time.
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());
}
});
Example 2C. BEST: When the grid is destroyed, the context menu is also destroyed.
3. Monster controllers
It’s amazing how many times we see apps that have one huge controller
with thousands of lines of code. We tend to favor breaking up our
controllers by app function. For example, an order processing app might
have individual controllers for line items, shipments, customer lookup,
etc. It makes navigating and maintaining the code much easier.
Some developers like to break out controllers by view. For example,
if an application had a grid and a form, there would be a controller to
manage the grid and a controller to manage the form. There is no one
“right” way to separate controller logic as long as you are consistent.
Just remember, controllers can communicate with other controllers. In
example 3A, you can see how to retrieve a reference to another
controller and call one of its methods.
this.getController('SomeOtherController').runSomeFunction(myParm);
Example 3A. Get a reference to another controller and call a method.
Alternately you could fire an application level event that any
controller can listen for. In examples 3B and 3C, you can see how one
controller can fire an app-level event and another controller can listen
for it.
MyApp.getApplication().fireEvent('myevent');
Example 3B. Fire an app-level event.
MyApp.getApplication().on({
myevent : doSomething
});
Example 3C. Another controller listens for the app-level event.
Note: Beginning with Ext JS 4.2, it gets even easier to use multiple
controllers -- they can fire events that other controllers can listen
for directly.
4. Poor folder structure for source code
This doesn’t affect performance or operation, but it makes it
difficult to follow the structure of your app. As your app grows,
finding source code and adding features and functionality will be much
easier if you organize your source code. We have seen that many
developers will put all views (even for a large app) in one folder as
shown in example 4A. We recommend adding organization to the views by
logical function as shown in example 4B.

Example 4A. BAD: All views are at one level.

Example 4B. GOOD: Views are organized by logical function.
5. Use of global variables
Even though it’s widely known that global variables are bad, we have
still see them used in some of the apps that we have recently reviewed.
Apps that use global variables can have significant problems with name
collisions and can be hard to debug. Instead of using global variables,
we will hold “properties” in a class and then reference those properties
with getters and setters.
For example, suppose your application needed to remember the last
selected customer. You might be tempted to define a variable in your app
as shown in example 5A. It’s easy and the value is conveniently
available to all parts of your app.
myLastCustomer = 123456;
Example 5A. BAD: Global variable created to store last customer number.
Instead, it’s a better practice to create a class that holds
properties intended to be used “globally.” In this case, we will create
a file called Runtime.js to hold runtime properties intended to change
as the app is used. Example 5B shows the location for Runtime.js in our
source code structure.

Example 5B. Location of the Runtime.js file.
Example 5C shows the contents of Runtime.js and example 5D shows how
to “require” it in your app.js. You can then “set” and “get” your
properties as shown in 5E and 5F from anywhere in the app.
Ext.define(‘MyApp.config.Runtime’,{
singleton : true,
config : {
myLastCustomer : 0 // initialize to 0
},
constructor : function(config){
this.initConfig(config);
}
});
Example 5C. Sample Runtime.js file to hold global properties for an app.
Ext.application({
name : ‘MyApp’,
requires : [‘MyApp.config.Runtime’],
...
});
Example 5D. Require the Runtime class in your app.js file.
MyApp.config.setMyLastCustomer(12345);
Example 5E. How to set the last customer.
MyApp.config.getMyLastCustomer();
Example 5F. How to get the last customer.
6. Use of “id”
We don’t recommend the use of id’s on components because each id must
be unique. It’s too easy to accidentally use the same id more than
once, which will cause duplicate DOM id’s (name collisions). Instead,
let the framework handle the generation of id’s for you. With Ext JS
ComponentQuery, there is no reason to ever have to specify an id on an
Ext JS component. Example 6A shows two code segments of an app where
there are two different save buttons created, both of which were
identified with an id of ‘savebutton’, causing a name collision.
Although obvious in the code below, it can be hard to identify name
collisions in a large application.
// 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'
}]
Example 6A. BAD: Assigning a duplicate ‘id’ to a component will cause a name collision.
Instead, if you want to manually identify each component you can
simply replace the ‘id’ with ‘itemId’ as shown in example 6B. This
resolves the name conflict, and we can can still get a reference to the
component via itemId. There are many ways to retrieve a reference to a
component via itemId. A few methods are shown in example 6C.
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’
}]
Example 6B. GOOD: Create components with an ‘itemId’.
var pictureSaveButton = Ext.ComponentQuery.query('#picturetoolbar > #savebutton')[0];
var orderSaveButton = Ext.ComponentQuery.query('#ordertoolbar > #savebutton')[0];
// assuming we have a reference to the “picturetoolbar” as picToolbar
picToolbar.down(‘#savebutton’);
Example 6C. GOOD: Referencing components by ‘itemId’.
7. Unreliable referencing of components
We sometimes see code that relies on component positioning in order
to get a reference. This should be avoided as the code can easily be
broken if any items are added, removed or nested within a different
component. Example 7A shows a couple common cases.
var mySaveButton = myToolbar.items.getAt(2);
var myWindow = myToolbar.ownerCt;
Example 7A. BAD: Avoid retrieving component references based on component positioning.
Instead, use ComponentQuery, or the component “up” or “down” methods,
to retrieve references as shown in example 7B. With this technique the
code will be less likely to break if the structure or ordering of
components is subsequently changed.
var mySaveButton = myToolbar.down(‘#savebutton’); // searching against itemId
var myWindow = myToolbar.up(‘window’);
Example 7B. GOOD: Use ComponentQuery to retrieve relative references.
8. Failing to follow upper/lowercase naming conventions
There are certain upper/lowercase standards that Sencha follows when
naming components, properties, xtypes, etc. To avoid confusion and to
keep your code clean, you should follow the same standards. Example 8A
shows several incorrect scenarios. Example 8B shows the same scenarios
with the correct upper/lowercase naming conventions.
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);
}
});
Example 8A. BAD: Areas in bold type have incorrect upper/lowercase naming.
Ext.define(‘MyApp.view.CustomerList’,{
extend : ‘Ext.grid.Panel’,
alias : ‘widget.customerlist’,
myCustomConfig : ‘xyz’,
initComponent : function(){
Ext.apply(this,{
store : ‘Customers’,
….
});
this.callParent(arguments);
}
});
Example 8B. GOOD: Areas in bold follow all of the correct upper/lowercase rules.
Additionally, if you are firing any custom events, the name of the
event should be all lowercase. Of course, everything will still work if
you don’t follow these conventions, but why stray outside of the
standards and write less clean code?
9. Constraining a component to a parent component’s layout
In example 9A, the panel will always have the ‘region:center’
property and thus would not work if you ever wanted to reuse the
component and put it in a “west” region, for example.
Ext.define('MyApp.view.MyGrid',{
extend : 'Ext.grid.Panel',
initComponent : function(){
Ext.apply(this,{
store : ‘MyStore’,
region : 'center',
......
});
this.callParent(arguments);
}
});
Example 9A. BAD: The ‘center’ region should not be specified here.
Instead, specify the layout configuration when creating the component
as shown in example 9B. This way, you can reuse the component anywhere
you like and not be constrained by the layout configuration.
Ext.define('MyApp.view.MyGrid',{
extend : 'Ext.grid.Panel',
initComponent : function(){
Ext.apply(this,{
store : ‘MyStore’,
......
});
}
});
// specify the region when the component is created...
Ext.create('MyApp.view.MyGrid',{
region : 'center'
});
Example 9B. GOOD: Specify the region when creating the component.
As shown in example 9C, you can also provide a default region on the component that could be overridden if necessary.
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
});
Example 9C. Also GOOD: Specify a default region and override if necessary.
10. Making your code more complicated than necessary
There are many times we see code that is more complicated than
necessary. This is usually a result of not being entirely familiar with
each component’s available methods. One of the most common cases we see
is code that loads each form field from a data record individually.
Example 10A shows an example of this.
// 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’));
Example 10A. BAD: Loading form fields from a record individually.
Instead of loading each value individually, use the loadRecord method
to load all fields from the record into the proper form fields with one
line of code. The key is to make sure the “name” property of the form
field matches the field name of the record as shown in example 10B.
items : [{
fieldLabel : ‘User’,
name : ‘UserName’
},{
fieldLabel : ‘Email’,
name : ‘Email’
},{
fieldLabel : ‘Home Address’,
name : ‘Address’
}];
myForm.loadRecord(record);
Example 10B. GOOD: Use loadRecord to load all form fields with one line of code.
- ext-all.js: minified, no docs/comments
- ext-all-debug.js: non-minified, no docs/comments
- ext-all-debug-w-comments.js: non-minified, with docs/comments
- ext-all-dev.js: non-minified, with docs/comments,
with console warnings, e.g. if you're using deprecated functions/configs
or there are configuration problems. The additional console output in
ext-all-dev.js can be really helpful, so I'd recommend it for development.
- ext-all-debug.js: (or
ext-all-debug-w-comments.js) is functionally the same as ext-all.js, allows better debugging since it's not minified. I'd recommend it for testing purposes.
- ext-all.js: obviously is what you want to use in production, since the file is much smaller than the other files.
Ext JS 5 will introduce a bunch of new features, including:
- Tablet support
- Two-way data binding
- New MVVM Application Architecture
- Widgets and the Widget Column
- Crisp Theme
- Routing
- and much more!