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 the 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;
- The variables should be listed in alphabetical order as shown below:
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 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
1 |
layout : 'vbox' |
or
1 2 3 4 |
layout : { type : 'vbox', align : 'middle' } |
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 using the layout config with an 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 the 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 the 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 the 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 a 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, the following declarations would result in the 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 the 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
- If the global variables can be grouped/categorized then put them under the most suitable category in a JSON object format
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“
The 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 cases 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 an 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 I will represent the component which created the current component
- “this” keyword used in the context of var is executed on the 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 an insignificantly bigger filesize, and that difference is eliminated to a 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 the statement. This makes it easier to add statements without accidentally introducing bugs.
-
123456789101112if(someVariableExists)x = false;anotherFunctionCall();// Above code is equivalent to below codeif(someVariableExists) {x = false;}anotherFunctionCall();// While the developer might have wanted to include this method inside if block
-
- The { (left curly brace) should be at the end of the line that begins the compound statement.
- 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.
-
12345compareParameters : function( firstParam, secondParam ) {return ( firstParam > secondParam );}// Notice the braces around the conditional statement, which is used for increasing readability.
-
Logging and Tracing
We often see developers using “console.log” in their application and – that’s 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 the production environment.
Further, these methods allow 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:
1 |
console.error("Error: %s (%i)", "Server is not responding:", 500); |
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 a 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 the new operator.
Controllers & Views
- Facts to be noticed
- While this is a fact, instead of a 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 the following order
- constructor
- initComponent
- The controller has 3 lifecycle methods – init, beforeInit and initViewModel. They are called in the following orders:
- beforeInit
- init
- initViewModel
- When combined the view and controller lifecycle methods are executed in the following order
- constructor of View
- beforeInit of Controller
- initComponent of View
- init of Controller
- initViewModel of Controller
- The views have life cycle methods like – constructor and initComponents and they are called in the following order
- While this is a fact, instead of a guideline, it is worth being aware of, while using lifecycle methods of controllers and views.
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 a few during any given session
- Avoid component nesting
- Many times people use panel inside the window and make similar mistakes elsewhere, which increases nesting as well as the size of the DOM
- 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
- 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 goals 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 the 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 the 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()
- ExtJS is a component library and one of its main goals 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 🙂
- Avoid usage of hard coded “id” property. While id may seem like giving 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 the possibility of using “itemId” config of components instead
- Instead of using images for buttons/tabs, make use of glyphs, 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
- The 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 the 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, the developer may create a context menu every time the 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 is 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 programmatic 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 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. The document must indicate if the property is mandatory or optional. The 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 a 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 the following 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