Introduction
Walking Tree has been using ExtJS for more than 6 years. In addition, being associated with Sencha at training level we do see us responsible for helping the community in using ExtJS in the best possible ways. When we started using ExtJS in 2008-2009, I personally compared it with Perl. Sounds strange!
Yep, Perl is amazingly powerful scripting language. Without appropriate guidelines / structure, it didn’t look so useful.However, if you wrap this under Object Oriented concepts then it looks awesome. Similarly, I personally never thought Javascript will dominate the language world, until we started using ExtJS. The continuous improvement in ExtJS – by community as well as Sencha has strengthened my belief further.
Javascript is a powerful language and ExtJS is a beautiful framework. However, care should be taken to write good clean code. We had our own struggle in the beginning and in that process we also had significant learning. Along the way, we have developed few guidelines, which I thought will be great to share with the community. I am putting these guidelines in this article. In case you do have an input which can improve this then do feel free to contribute.
Declaration and Convention
Variables
Naming Convention
Names should be formed from the 26 upper and lower case letters (A .. Z, a .. z), the 10 digits (0 .. 9), and _ (underscore).
Avoid use of international characters because they may not read well or be understood everywhere.
Do not use $ (dollar sign) or \ (backslash) in names
Always begin your variable names with a letter, not “$” or “_”.
Use full and meaningful words instead of cryptic abbreviations. Doing so will make your code easier to read and understand. In many cases it will also make your code self-documenting
Keep in mind that the name you choose must not be a keyword or reserved word
If the name you choose consists of only one word, spell that word in all lowercase letters
Use lowerCamelCase (e.g. calculatedRatio) notation for variables with more than one word in its name
Variable Declarations
All variables should be declared before used.
JavaScript does not require this, but doing so makes the program easier to read and makes it easier to detect undeclared variables that may become implied globals or that may not be initialized but used.
Implied global variables should never be used
Also, the use of global variables must be minimized
The var statements should be the first statements in the function body
JavaScript does not have block scope, so defining variables in blocks can confuse programmers who are experienced with other C family languages. Define all variables at the top of the function.
It is preferred that each variable be given its own line and comment. This is one thing which I see that people start debating, however, I do hate to see multiple variables being declared (and initialized) on the same line.
The variables should be listed in alphabetical order as shown below:
var currentEntry = -1;
var level = 0;
Class Names
The class name follows similar naming convention as variable names – except that they follow UpperCamelCase notation.
A class must be defined in its own file and the file name shall exactly match the class name. Use ExtJS structure to give package name for better organisation of files in a larger projects.
Event Naming Convention
The event name must be in all lower cases. We must not use the camelCase convention. For example onAddCommunication or addCommunication is not good, while the event name addcommunication will be considered good
Using Layout or Similar Potentially Compound Declarations
While declaring the layout for a container ExtJS allows you to use one of the following approaches
or
While the later declaration becomes mandatory, in case you do want to use custom values for the properties, the presence of two separate ways of declarations make it confusing for the beginners. Also, since it doesn’t add too much to the space savings, I personally recommend to use layout config with explicit type property.
View Models and Data Binding
Prefer Using Declarations over procedural calls
While you can use “bind()” method on the component (remember all the components are bindable) to bind a view with its model, it is cleaner to specify bind properties in declaration itself.
Use “deep:true” declaration when you are binding a value with an object / property embedded inside another object
When you have derived variables, use formula configuration of the ViewModel. Use get and set function declarations appropriately to support two-way derivation
Form Validation and Submission
Use models for validating and submitting the forms and push the field validation logic (including custom validations) inside models
Use loadRecord method of the FormPanel instead of setting the form fields individually. Setting individual fields is error prone as and it makes the code look cluttered
Instead of using explicit logic to enable / disable a submit button, make use of “formBind: true”
Other Common Tips
The “defaults” config allows you to mention the default values that you would like to specify for the child items of a container.
Quite often I see that people don’t use this and instead repeat same configuration and their values across different child elements.
Further many times people mention “xtype : ‘panel'” when the panel might already be the default xtype for the given item
The controllers are only associated with their views (from ExtJS 5 onwards). Hence don’t try to force fit something which may not be directly coming from the view. Otherwise, it will create abstraction and maintainability will be compromised
Whenever you are using a component, it will be a good idea to include that into “requires” section to avoid any runtime warning.
Scopes
Correct understanding and usage of “scope” is crucial for effective use of Javascript in general and ExtJS in particular. Pay close attention to following:
Global Variables
Build a habit of always using “var” keyword for declaring variable so that appropriate scope gets considered.
If you don’t use “var” keyword while defining the variable then it is considered as global variable
The variables declared outside a function is considered in global scope – irrespective of the usage of “var” keyword
These variables are global “window” namespace. For example following declarations would result into same thing, however the one with “window” namespace being mentioned explicitly is considered a better practice.
var gCompanyName = “Walking Tree”;
window.companyName = “Walking Tree”;
If you do want to define global variable then create a separate file and put your variables into that
If the global variables can be grouped / categorized then put them under most suitable category in a JSON object format
This is often useful for defining labels, titles, IDs, presets
Never hard code strings into program codes, component configurations or templates
this or that
Coming from C++ / Java background, I always found scoping in ExtJS cryptic. The usage of me, this, that, etc by the experienced developers were forcing me to think that there has to be a logic.
Two rules for “this”
When a function is executed via a var reference, the default execution context is “window“
When a function is executed via an object key, the execution context is “the object“
Following table shows codes in var reference and object key reference:
Following guidelines and simplification of understanding would help:
You may like to consider using saving the “this” scope into a different (and smaller) name (e.g. me), which can be considered for minification as well. Some of the case where it may look very useful are:
When you do have a need for referring to different components from an existing component and write certain logic (e.g. you may like to define inline handler for an event on an instance of a component). In such cases the “this” scope would represent the scope related to current component / object, while me will represent the component which created the current component
“this” keyword used in the context of var is executed on window object and when it is executed on an object key then it is in the context of that specific object
Readability Guidelines
The unit of indentation must be four spaces.
Use of tabs should be avoided because there still is not a standard for the placement of tabstops. The use of spaces can produce a insignificantly bigger filesize, and that difference is eliminated to large extent by minification.
JavaScript programs should be stored in and delivered as .js files.
Avoid lines longer than 80 characters. When a statement will not fit on a single line, it may be necessary to break it.
Place the line break after an operator, ideally after a comma.
A break after an operator decreases the likelihood that a copy-paste error will be masked by semicolon insertion.
The next line should be indented 8 spaces.
The comments should be well-written and clear. It shall not duplicate the code statements rather it shall complement the code and put together it shall provide 100% completeness. It is important that comments are kept up-to-date and whenever there is a change in the related code, the comment must ensure that it is still valid and consistent with the changed code.
The comments like i = 0; // Set i to zero, is irrelevant and undesirable.
Generally use line comments (//).
Save block comments (/* */) for formal documentation and for commenting out
Blank lines improve readability by setting off sections of code that are logically related
Blank spaces should be used in the following circumstances:
A keyword followed by ( (left parenthesis) should be separated by a space.
A blank space should not be used between a function value and its ( (left parenthesis). This helps to distinguish between keywords and function invocations.
All binary operators except . (period) and ( (left parenthesis) and [ (left bracket) should be separated from their operands by a space.
No space should separate a unary operator and its operand except when the operator is a word such as typeof
Each ; (semicolon) in the control part of a for statement should be followed by a space.
Whitespace should follow every , (comma).
Logic and Statements Guidelines
Comparison and Assignment
Use explicit equality comparators (ie “===”) over coerced comparators (ie “==”)
Use explicit inequality comparators (ie “!==”) over coerced comparators (ie “!=”)
Use shortcuts for default value assignment, which make your code more faster as well as more readable. For example below assignment looks much cleaner
var greeting = args.greeting || “Good day”;
For the compound statements, the enclosed statements should be indented four spaces
The { (left curly brace) should be at the end of the line that begins the compound statement.
The } (right curly brace) should begin a line and be indented to align with the beginning of the line containing the matching { (left curly brace).
Braces should be used around all statements, even single statements, when they are part of a control structure, such as an if or for statement. This makes it easier to add statements without accidentally introducing bugs.
In a simple comparison operator many times people end up using if-else statement or tertiary operator to eventually return a true / false value based on comparing the parameter(s). In such cases returning the result of the conditional evaluation is the best thing to do.
Logging and Tracing
We often see developers using “console.log” in their application and – thats it. While this may be okay during development and test build, a good packaging tool might remove it from the minified / production ready code. Hence it is important to know the available options and their level and use them appropriately. Console support various logging level and here are some of the key ones
info
debug
log
warn
error
The last two must be used as required to be able to diagnose the problem in production environment.
Further, these methods allows you to mention parameters in different formats as arguments. However, often developers use the concatenation operators to create a string. This is a bad practice. Following is a sample example of how an error logging should be taken care:
Statements
Each line should contain at most one statement.
Put a ; (semicolon) at the end of every simple statement.
Note that an assignment statement which is assigning a function literal or object literal is still an assignment statement and must end with a semicolon.
Lifecycle Guidelines
new operator
Use {} instead of new Object()
Use [] instead of new Array().
For ExtJS objects, you shall use create method
In nutshell, avoid using new.
For dates related objects, you might still be required to use new. However, for other types of data, you shall not be required to use new operator.
Controllers & Views
Facts to be noticed
While this is a fact, instead of guideline, it is worth being aware of, while using lifecycle methods of controllers and views.
The views have life cycle methods like – constructor and initComponents and they are called in following order
constructor
initComponent
The controller have 3 lifecycle methods – init, beforeInit and initViewModel. They are called in following orders:
beforeInit
init
initViewModel
When combined the view and controller lifecycle methods are executed in following order
constructor of View
beforeInit of Controller
initComponent of View
init of Controller
initViewModel of Controller
Performance Guidelines
Keep DOM as small as possible
Using “deferredRender:true” declaration you can ensure that the tabs of the tabpanel (for that matter any container using the card layout) will be rendered at the time it becomes active. This is pretty helpful when
there is a significant amount of content or
a lot of heavy controls or
there are many tabs and user may access only few during any given session
Avoid component nesting
Many times people use panel inside window and make similar mistakes elsewhere, which increases nesting as well as size of the DOM
Ext JS Panels are more powerful (and expensive!) than basic Containers and you may not need them all the time. So specify xtype: ‘container’ to avoid having your application use the default ‘panel’ unless you are sure that you need a panel.
Minimize DOM access / manipulation
ExtJS is a component library and one of its main goal is to insulate you from directly manipulating DOM. So, whenever you do have such need, there is nothing wrong in being a little more alert :-)
Always consider using Sencha classes which wraps the native javascript DOM methods, otherwise you may end up facing browser compatibility issues
While using query method, often people use Ext.ComponentQuery.query without root elements. This means that all Components within the document are included in the search and thus the search become costlier.
Depending on context, consider using container’s “down” or “child” and component’s “up” method to minimize the number of search components
Avoid using document.getElementById()
Avoid using Ext.getCmp()
Avoid usage of hard coded “id” property. While id may seem like giving a quick access to the element, it is easier to commit mistakes in enterprise application and often it is very costly to identify and rectify such problems.
In case it is being used, there must be a strong reason to do so and explicit comments must be specified.
Evaluate possibility of using “itemId” config of components instead
Instead of using images for buttons / tabs, make use of glyps, wherever applicable
Make use of object references while accessing an object / property deeply nested inside an object
Specifically this is extremely important when such properties are getting accessed / used inside a loop
Similarly, when you loop on array size then keep the size calculated outside the loop
Idea is simple – keep computation-heavy code outside the loop
Make use of object references while handling events
All the event handler have the component reference being passed as the first parameter of the method. Make use of that in case you need to change anything on the view
Also, in case you do have a need to update some other view / component then specify reference on that component and make use of the lookupReference() method in controller
Must avoid Memory Leaks
Some times developers create a new object and they don’t bother about reusing the already created object. For example, developer may create a context menu every time user right clicks. This is a bad practice. Instead it should be created once and reused whenever possible.
Use console.time(“YourTimer”) method to determine how long an operation takes.
General Guidelines
Ext.apply Copies all the properties of config to the specified object. Note that if recursive merging and cloning without referencing the original object / array is needed, use Ext.Object.merge instead.
You must avoid hard coding of height, width
Use arrays when the member names would be sequential integers. Use objects when the member names are arbitrary strings or names
Watch out for the extra comma which are not required in your code
Never leave debugger statement in a code
Never user “eval()”
While most of the browsers support Javascript by default, while designing enterprise solutions, you must assume that Javascript may be disabled and accordingly you shall implement the solution
Think of your code as a story and make it easier for people to read
Use CSS classes to keep the look and feel to the CSS designer. Strictly avoid any programatic styling which you can achieve through simple CSS selectors and / or inheritances
If you find yourself creating lots and lots of HTML in ExtJS, you might be doing something wrong. XTemplate does expect you to put HTML code, and a good practice is to keep all such fragments in a common place.
Although MVC is still supported in ExtJS, use the new MVVC / MVVM pattern as it separates the concerns really nicely, which is desired for an enterprise level applications
Documentation
Code documentation must be done using JSDuck annotations. Following are the minimal documentation requirement:
Every class must be documented
Every public and protected property of a class must be documented along with an example value. Document must indicate if the property is mandatory or optional. Default value must be specified for the optional properties.
Every public and protected method of a class must be documented
Events fired by a class must be documented along with the parameters that will be passed to a listener
JSDuck document must be generated from the documented code.
Summary
In this blog, I have tried to put consolidated set of rules / guidelines that you would like yourself and your team to follow. I hope this article helps you write better code and your productivity increases significantly. While this article will help in bridging the knowledge gap, usage of JS Lint or equivalent tool will definitely add more value. I am definitely considering this article as a running document and it will change over a period. Feel free to shoot your comment and suggestions!
Walking Tree is a Sencha Service and Training Partner. We would love to help you in making effective use of the framework.
You can reach us by filling in following form:
[contact-form]
Reference
http://javascript.crockford.com/code.html
https://vimeo.com/79436259
http://www.htmlgoodies.com/beyond/javascript/variable-naming-conventions-in-javascript.html
http://dailyjs.com/2012/07/23/js101-scope/
http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
https://developer.chrome.com/devtools/docs/console-api