Many classes have shortcut names used when creating (instantiating) a class with a
configuration object. The shortcut name is referred to as an alias
(or xtype
if the
class extends Ext.Component). The alias/xtype is listed next to the class name of
applicable classes for quick reference.
Framework classes or their members may be specified as private
or protected
. Else,
the class / member is public
. Public
, protected
, and private
are access
descriptors used to convey how and when the class or class member should be used.
Public classes and class members are available for use by any other class or application code and may be relied upon as a stable and persistent within major product versions. Public classes and members may safely be extended via a subclass.
Protected class members are stable public
members intended to be used by the
owning class or its subclasses. Protected members may safely be extended via a subclass.
Private classes and class members are used internally by the framework and are not intended to be used by application developers. Private classes and members may change or be omitted from the framework at any time without notice and should not be relied upon in application logic.
static
label next to the
method name. *See Static below.Below is an example class member that we can disect to show the syntax of a class member (the lookupComponent method as viewed from the Ext.button.Button class in this case).
Let's look at each part of the member row:
lookupComponent
in this example)( item )
in this example)Ext.Component
in this case). This may be omitted for methods that do not
return anything other than undefined
or may display as multiple possible values
separated by a forward slash /
signifying that what is returned may depend on the
results of the method call (i.e. a method may return a Component if a get method calls is
successful or false
if unsuccessful which would be displayed as
Ext.Component/Boolean
).PROTECTED
in
this example - see the Flags section below)Ext.container.Container
in this example). The source
class will be displayed as a blue link if the member originates from the current class
and gray if it is inherited from an ancestor or mixed-in class.view source
in the example)item : Object
in the example).undefined
a "Returns" section
will note the type of class or object returned and a description (Ext.Component
in the
example)Available since 3.4.0
- not pictured in
the example) just after the member descriptionDefaults to: false
)The API documentation uses a number of flags to further commnicate the class member's function and intent. The label may be represented by a text label, an abbreviation, or an icon.
classInstance.method1().method2().etc();
false
is returned from
an event handler- Indicates a framework class
- A singleton framework class. *See the singleton flag for more information
- A component-type framework class (any class within the Ext JS framework that extends Ext.Component)
- Indicates that the class, member, or guide is new in the currently viewed version
- Indicates a class member of type config
- Indicates a class member of type property
- Indicates a class member of type
method
- Indicates a class member of type event
- Indicates a class member of type
theme variable
- Indicates a class member of type
theme mixin
- Indicates that the class, member, or guide is new in the currently viewed version
Just below the class name on an API doc page is a row of buttons corresponding to the types of members owned by the current class. Each button shows a count of members by type (this count is updated as filters are applied). Clicking the button will navigate you to that member section. Hovering over the member-type button will reveal a popup menu of all members of that type for quick navigation.
Getting and setter methods that correlate to a class config option will show up in the methods section as well as in the configs section of both the API doc and the member-type menus just beneath the config they work with. The getter and setter method documentation will be found in the config row for easy reference.
Your page history is kept in localstorage and displayed (using the available real estate) just below the top title bar. By default, the only search results shown are the pages matching the product / version you're currently viewing. You can expand what is displayed by clicking on the button on the right-hand side of the history bar and choosing the "All" radio option. This will show all recent pages in the history bar for all products / versions.
Within the history config menu you will also see a listing of your recent page visits. The results are filtered by the "Current Product / Version" and "All" radio options. Clicking on the button will clear the history bar as well as the history kept in local storage.
If "All" is selected in the history config menu the checkbox option for "Show product details in the history bar" will be enabled. When checked, the product/version for each historic page will show alongside the page name in the history bar. Hovering the cursor over the page names in the history bar will also show the product/version as a tooltip.
Both API docs and guides can be searched for using the search field at the top of the page.
On API doc pages there is also a filter input field that filters the member rows using the filter string. In addition to filtering by string you can filter the class members by access level, inheritance, and read only. This is done using the checkboxes at the top of the page.
The checkbox at the bottom of the API class navigation tree filters the class list to include or exclude private classes.
Clicking on an empty search field will show your last 10 searches for quick navigation.
Each API doc page (with the exception of Javascript primitives pages) has a menu view of metadata relating to that class. This metadata view will have one or more of the following:
Ext.button.Button
class has an alternate class name of Ext.Button
). Alternate class
names are commonly maintained for backward compatibility.Runnable examples (Fiddles) are expanded on a page by default. You can collapse and expand example code blocks individually using the arrow on the top-left of the code block. You can also toggle the collapse state of all examples using the toggle button on the top-right of the page. The toggle-all state will be remembered between page loads.
Class members are collapsed on a page by default. You can expand and collapse members using the arrow icon on the left of the member row or globally using the expand / collapse all toggle button top-right.
Viewing the docs on narrower screens or browsers will result in a view optimized for a smaller form factor. The primary differences between the desktop and "mobile" view are:
The class source can be viewed by clicking on the class name at the top of an API doc page. The source for class members can be viewed by clicking on the "view source" link on the right-hand side of the member row.
One of the major components in Sencha Cmd is its compiler. This guide describes how to write code that gets the most out of the compiler and prepares for future framework-aware optimizations.
The following guides are recommended reading before proceeding further:
Sencha Cmd compiler is not a replacement for tools like these:
These tools solve different problems for JavaScript developers and are
very good at the world of JavaScript, but have no understanding of Sencha framework
features
such as Ext.define
for declaring classes.
The role of the Sencha Cmd compiler is to provide framework-aware optimizations and diagnostics. Once code has passed through the Sencha Cmd compiler, it is ready for more general tools.
These kinds of optimizations have shown to significantly improve the "ingest" time of JavaScript code by the browser, especially on legacy browsers.
For the compiler to provide these benefits, however, it is now important to look at the coding conventions that the compiler can "understand" and therefore optimize for you. Following the conventions described in this guide ensure that your code is positioned to get the most from Sencha Cmd today and in the future.
The dynamic loader and the previous JSBuilder have always made certain assumptions about how classes are organized, but they were not seriously impacted by failure to follow those guidelines. These guidelines are very similar to Java.
To recap, these guidelines are:
Ext.define
statement at global scope.Ext.define("MyApp.foo.bar.Thing", ...
is
"Thing.js".Ext.define("MyApp.foo.bar.Thing", ...
, the source file
is in a path ending with "/foo/bar".Internally, the compiler views source files and classes as basically synonymous. It makes no attempt to split up files to remove classes that are not required. Only complete files are selected and included in the output. This means that if any class in a source file is required, all classes in the file will be included in the output.
To give the compiler the freedom to select code at the class-level, it is essential to put only one class in each file.
The Sencha Class System provides the Ext.define
function to enable high-level, object
oriented programming. The compiler takes the view that Ext.define
is really a form of
"declarative" programming and processes the "class declaration" accordingly.
Clearly if Ext.define
is understood as a declaration, the content of the class body
cannot
be constructed dynamically in code. While this practice is rare, it is valid JavaScript.
But as we shall see below in the code forms, this is antithetical to the compiler's
ability to understand the code it parses. Dynamic class declarations are often used to do
things that are better handled by other features of the compiler. For more on these
features, see the Sencha Compiler Reference.
The compiler understands these "keywords" of this declarative language:
requires
uses
extend
mixins
statics
alias
singleton
override
alternateClassName
xtype
For the compiler to recognize your class declarations, they need to follow one of the following forms.
Most classes use simple declarations like this:
Ext.define('Foo.bar.Thing', {
// keywords go here ... such as:
extend: '...',
// ...
});
The second argument is the class body which is processed by the compiler as the class "declaration".
Note: In all forms, call Ext.define
at global scope.
In some use cases the class declaration is wrapped in a function to create a closure scope
for the class methods. In all of the various forms, it is critical for the compiler that
the function end with a return
statement that returns the class body as an object
literal. Other techniques are not recognized by the compiler.
To streamline the older forms of this technique described below, Ext.define
understands
that if given a function as its second argument, that it should invoke that function to
produce the class body. It also passes the reference to the class as the single argument
to facilitate access to static members via the closure scope. Internally to the framework,
this was the most common reason for the closure scope.
Ext.define('Foo.bar.Thing', function (Thing) {
return {
// keywords go here ... such as:
extend: '...',
// ...
};
});
Note: This form is only supported in Ext JS 4.1.2 and later and Sencha Touch 2.1 and later.
In previous releases, the "Function Form" was not supported, so the function was simply invoked immediately:
Ext.define('Foo.bar.Thing', function () {
return {
// keywords go here ... such as:
extend: '...',
// ...
};
}());
This form and the next are commonly used to appease tools like JSHint (or JSLint).
Ext.define('Foo.bar.Thing', (function () {
return {
// keywords go here ... such as:
extend: '...',
// ...
};
})());
Another variation on immediately called "Function Form" to appease JSHint/JSLint.
Ext.define('Foo.bar.Thing', (function () {
return {
// keywords go here ... such as:
extend: '...',
// ...
};
}()));
The class declaration in its many forms ultimately contains "keywords". Each keyword has its own semantics, but there are many that have a common "shape".
The extend
and override
keywords only accept a string literal.
These keywords are also mutually exclusive in that only one can be used in any declaration.
The following keywords all have the same form:
requires
uses
alias
alternateClassName
xtype
The supported forms for these keywords are as follows.
Just a string:
requires: 'Foo.thing.Bar',
//...
An array of strings:
requires: [ 'Foo.thing.Bar', 'Foo.other.Thing' ],
//...
mixins
Using an object literal, the name given the mixin can be quoted or not:
mixins: {
name: 'Foo.bar.Mixin',
'other': 'Foo.other.Mixin'
},
//...
Mixins can also be specified as a String[]:
mixins: [
'Foo.bar.Mixin',
'Foo.other.Mixin'
],
//...
This approach relies on the mixinId
of the mixin class but also allows the receiving
class to control the mixin order. This is important if the mixins have overlapping
methods or properties and the receiving class wants to control which mixin supplies the
overlapping methods or properties.
statics
KeywordThis keyword places properties or methods on the class, as opposed to on each of the instances. This must be an object literal.
statics: {
// members go here
},
// ...
singleton
KeywordThis keyword was historically only used with a boolean "true" value:
singleton: true,
The following (redundant) use is also supported:
singleton: false,
In Ext JS 4.1.0 and Sencha Touch 2.0, Ext.define
gained the ability to manage overrides.
Historically, overrides have been used to patch code to work around bugs or add
enhancements. This use was complicated with the introduction of the dynamic loader because
of the timing required to execute the Ext.override
method. Also, in large applications
with many overrides, not all overrides in the code base were needed by all pages or builds
(for example, if the target class was not required).
All this changed once the class system and loader understood overrides. This trend only continues with Sencha Cmd. The compiler understands overrides and their dependency effects and load-sequence issues.
In the future, the compiler will become even more aggressive at dead-code elimination of methods replaced by an override. Using managed overrides as described below enables this optimization of your code once it's available in Sencha Cmd.
Below is the standard form of an override. The choice of namespace is somewhat arbitrary, but see below for suggestions.
Ext.define('MyApp.patches.grid.Panel', {
override: 'Ext.grid.Panel',
...
});
With the ability to use Ext.define
to manage overrides, new idioms have opened up and
are actively being leveraged. For example in the code generators of
Sencha Architect and internal to the
framework,
that break apart large classes like Ext.Element
into more manageable and cohesive
pieces.
Overrides as patches are the historical use case and hence the most common in practice today.
Caution: Take care when patching code. While the use of override itself is supported, the end result of overriding framework methods is not supported. All overrides should be carefully reviewed whenever upgrading to a new framework version.
That said, it is, at times, necessary to override framework methods. The most common case for this to fix a bug. The Standard Override Form is ideal in this case. In fact, Sencha Support will at times provide customer with patches in this form. Once provided, however, managing such patches and removing them when no longer needed, is a matter for the review process previously mentioned.
Organize patches in a namespace associated with the top-level namespace of the target. For example, "MyApp.patches" targets the "Ext" namespace. If third party code is involved then perhaps another level or namespace should be chosen to correspond to its top-level namespace. From there, name the override using a matching name and sub-namespace. In the previous example:
Ext -> MyApp.patches).grid.Panel
When dealing with code generation (as in Sencha Architect), it is common for a class to consist of two parts: one machine generated and one human edited. In some languages, there is formal support for the notion of a "partial class" or a class-in-two-parts.
Using an override, you can manage this cleanly:
In ./foo/bar/Thing.js
:
Ext.define('Foo.bar.Thing', {
// NOTE: This class is generated - DO NOT EDIT...
requires: [
'Foo.bar.custom.Thing'
],
method: function () {
// some generated method
},
...
});
In ./foo/bar/custom/Thing.js
:
Ext.define('Foo.bar.custom.Thing', {
override: 'Foo.bar.Thing',
method: function () {
this.callParent(); // calls generated method
...
},
...
});
Naming Recommendations:
Foo.bar.ThingOverride
or Foo.bar.ThingGenerated
so that the parts of a class
collate together in listings.A common problem for base classes in object-oriented designs is the "fat base class". This happens because some behaviors apply across all classes. When these behaviors (or features) are not needed, however, they cannot be readily removed if they are implemented as part of some large base class.
Using overrides, these features can be collected in their own hierarchy and then
requires
can be used to select these features when needed.
In ./foo/feature/Component.js
:
Ext.define('Foo.feature.Component', {
override: 'Ext.Component',
...
});
In ./foo/feature/grid/Panel.js
:
Ext.define('Foo.feature.grid.Panel', {
override: 'Ext.grid.Panel',
requires: [
'Foo.feature.Component' // since overrides do not "extend" each other
],
...
});
This feature can be used now by requiring it:
...
requires: [
'Foo.feature.grid.Panel'
]
Or with a proper "bootstrap" file (see Workspaces in Sencha Cmd
...
requires: [
'Foo.feature.*'
]
Naming Recommendation:
requires
and uses
in an OverrideThese keywords are supported in overrides. Use of requires
may limit the compiler's
ability to reorder the code of an override.
callParent
and callSuper
To support all of these new uses cases, callParent
was enhanced in Ext JS 4.0 and Sencha
Touch 2.0 to "call the next method". The "next method" may be an overridden method or an
inherited method. As long as there is a next method, callParent
will call it.
Another way to view this is that callParent
works the same for all forms of
Ext.define
,
be they classes or overrides.
While this helped in some areas, it unfortunately made bypassing the original method (as a
patch or bug fix) more difficult. Ext JS 4.1 and later and Sencha Touch 2.1 and later
provides a method named callSuper
that can bypass an overridden method.
In future releases, the compiler will use this semantic difference to perform dead-code elimination of overridden methods.
Starting in version 4.2.2, overrides can declare their compatibility
based on the
framework version or on versions of other packages. This can be useful for selectively
applying patches that are safely ignored when they are incompatible with the target class
version.
The simplest use case is to test framework version for compatibility:
Ext.define('App.overrides.grid.Panel', {
override: 'Ext.grid.Panel',
compatibility: '4.2.2', // only if framework version is 4.2.2
//...
});
An array is treated as an OR, so if any specs match, the override is compatible.
Ext.define('App.overrides.some.Thing', {
override: 'Foo.some.Thing',
compatibility: [
'4.2.2',
'[email protected]'
],
//...
});
To require that all specifications match, an object can be provided:
Ext.define('App.overrides.some.Thing', {
override: 'Foo.some.Thing',
compatibility: {
and: [
'4.2.2',
'[email protected]'
]
},
//...
});
Because the object form is just a recursive check, these can be nested:
Ext.define('App.overrides.some.Thing', {
override: 'Foo.some.Thing',
compatibility: {
and: [
'4.2.2', // exactly version 4.2.2 of the framework *AND*
{
// either (or both) of these package specs:
or: [
'[email protected]',
'[email protected]+'
]
}
]
},
//...
});
For details on version syntax, see the checkVersion
method of Ext.Version
.
As Sencha Cmd continues to evolve, it continues to introduce new diagnostic messages to help point out deviations from these guidelines.
A good place to start is to see how this information can help inform your own internal code style guidelines and practices.