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.
Ext JS 6.2 introduced the Ext.drag API to provide a cross-toolkit solution to drag-and-drop for the Calendar package. This API provides a collection of classes that allow applications to easily add custom Drag/Drop functionality ranging from basic element manipulation to complex, aysnchronous data transfer. The Ext.drag API is modeled after and greatly expands on the HTML5 drag/drop API and is available for both Classic and Modern Toolkits.
Ext.drag keeps the element-level API simple with the appropriate hooks to combine with your components. It also allows data related actions to occur asynchronously.
Ext.drag is composed of two main classes: Ext.drag.Source and Ext.drag.Target. A source is something that can be dragged. A target is something that can receive a drop from a source. Both of these classes are attached to an Ext.dom.Element.
This namespace also includes functionality that handles element-level interactions. For components, it is often useful to wrap these classes to provide a more component friendly interface.
A drag source represents a movable element that can be dragged on screen. Below are some of the main features:
Constraints limit the possible positions a drag can occur. Constraints for sources are handled by the Ext.drag.Constraint class. A configuration for this (along with some shortcuts) can be passed to the drag source. Some of the useful options include:
Limiting the drag to be only horizontal or vertical
Limiting the drag to a particular onscreen region (Ext.util.Region)
Limiting the drag to a parent element
Limiting the position of the drag by snapping to a grid in increments.
You can see an example of constraints being applied in our Kitchen Sink Example
By default, a drag gesture on any portion of the source element will initiate a drag on the source. A handle allows specific portion/s of the element to be specified by using a css selector. This is useful in 2 main scenarios:
The source should only be dragged in a certain area, for example, the title bar of a window.
The source has many repeated elements that should trigger a drag with unique data, for example a dataview.
You can see an example of constraints being applied in our Kitchen Sink Example
A drag proxy is a visual representation show on screen while a drag is in progress. The proxy element (if specified) follows the mouse cursor. There are currently 3 implementations provided in this namespace:
Original (the default) - The element of the source is moved.
Placeholder - A new element is created and the source element is left in place.
None - No proxy element is shown. This is typically used in conjunction with Source events to display drag information.
The following events and template methods on the source are available at the specified points in the drag cycle:
beforedragstart/onBeforeDragStart
, can be cancelled by returning false.
dragstart
/onDragStart
dragmove
/onDragMove
dragend
/onDragEnd
dragcancel
/onDragCancel
var logger = Ext.getBody().createChild({
tag: 'textarea',
rows: 15,
cols: 80
}).dom;
function log(eventName, type) {
return function(item) {
var val = logger.value;
if (val.length) {
val += '\n';
}
val += '"' + eventName + '" fired on ' + type;
logger.value = val;
logger.scrollTop = logger.scrollHeight;
};
}
new Ext.drag.Source({
element: Ext.getBody().createChild({
html: 'Drag Me',
style: {
zIndex: 10,
width: '100px',
height: '100px',
border: '1px solid red',
position: 'absolute',
top: '50px',
left: '600px'
}
}),
listeners: {
beforedragstart: log('beforedragstart', 'Source'),
dragstart: log('dragstart', 'Source'),
dragmove: log('dragmove', 'Source'),
dragend: log('dragend', 'Source')
}
});
new Ext.drag.Target({
element: Ext.getBody().createChild({
html: 'Drop Here',
style: {
width: '300px',
height: '300px',
border: '1px solid blue',
position: 'absolute',
top: '250px',
left: '600px'
}
}),
listeners: {
dragenter: log('dragenter', 'Target'),
dragmove: log('dragmove', 'Target'),
dragleave: log('dragleave', 'Target'),
beforedrop: log('beforedrop', 'Target'),
drop: log('drop', 'Target')
}
});
A drag target represents an element that can receive a drop from a source. Most of its functionality will be described in the data exchange and interaction between sources/targets sections.
The following events and template methods on the source are available at the specified points in the drag cycle:
dragenter
/onDragEnter
dragmove
/onDragMove
dragleave
/onDragLeave
beforedrop
/onBeforeDrop
, can be cancelled by returning false.
drop
/onDrop
This class acts as a mediator and information holder for the lifetime of a single drag. It holds all information regarding a particular drag. It also manages data exchange for the drag. This class is passed to all relevant events/template methods throughout the drag/drop cycle.
Drag/drop provides the mechanics for moving elements and receiving events, however it doesn't describe the underlying meaning of those actions. This section discusses working with that data.
Similar to the HTML5 drag API, data is specified as a set key/value pair. The key is used to indicate the type of
data being passed. The key is is not restricted in value, but will typically refer to the data type (text/image) or
the business logic objects (users/orders). There can be many key/value pairs for a single drag operation. For each key
added to the setData
method, it will also be added to the types
Array on the info object to interrogate the
available types.
This architecture is useful for several reasons:
It allows targets to quickly decide whether or not they have interest in a particular source. If some drag data is marked as "csv", a drop target that is a table or grid may be able to consume that data, but it will probably not be useful to a text field.
It allows the data to differ depending on the target. Consider an image type being dragged. 2 keys could be set, to allow consumption by differing targets. When the drop occurs on a text field target, it can consume the text data. On a placeholder image target, it can read the blob data and display the image:
describe: function(info) {
// The text link to the image
info.setData('text', theImage.link);
// The binary image data
info.setData('image', toBlob(theImage));
}
Data is available for consumption via the getData
method on the info object. This method will throw an exception if
it is called before the drop completes. Only the type of data may be interrogated beforehand. This is done for 2 reasons:
For consistency with the HTML5 drag API (the same restriction applies)
The data may be expensive to produce or need to be retrieved from a remote source, so it is useful to restrict access until it's required.
However, it is still possible to have data on the info object that is accessible during the drag. The info object persists throughout the entire drag, so adding properties can occur at any point:
describe: function(info) {
info.userRecords = getTheRecords();
}
When a drag initiates, the describe
method is called on the source. The data for the drag should be specified here.
This is expected to be implemented by the user. The describe
method receives the info
object.
The setData
method is called with 2 parameters, a string key that describes the data and a value. The value can be
any data value data type (string, number, object, array). It can also be a function which will be executed to produce
the data when a call to getData
is made. Note that calls to getData
are limited to when the drop completes.
describe: function(info) {
// Set immediately available data
info.setData('userId', user.id);
// Setup a function to retrieve the data from the server on drop
info.setData('userInfo', function() {
var options = {}; // ajax options
return Ext.Ajax.request(o);
});
}
Alternatively, if the data is immediately available or easy to construct it can be pushed as a property directly on the info object. This data will be available at any time during the lifetime of the drag operation.
describe: function(info) {
info.userRecords = getTheRecords();
}
getData should be called from the drop
listener or onDrop
template method to retrieve data. This method accepts a
single argument, the key of the data specified from setData
. The value returned from getData
will always be a
Promise
, regardless of the underlying type. Using the data-set from above:
listeners: {
drop: function(target, info) {
info.getData('userId').then(function(v) {
console.log(v);
});
info.getData('userInfo').then(function(response) {
// The ajax response
}).catch(function() {
// Oh no!
});
}
}
In this case, the userId
value will be available so the promise will resolve immediately. In the case of the ajax
data, it will wait until the request returns.
For properties stored on the info object, they can be accessed using normal property access:
listeners: {
drop: function(target, info) {
console.log(info.userRecords);
}
}
By default, all sources can interact with all targets. This can be restricted in a number of ways. A drag on a target
is considered valid once a series of conditions is met. Targets will still receive events for invalid targets, however
the valid
flag on the info object will be false
.
If a Source is disabled, it is not possible to drag. If a Target is disabled, any source is not valid.
Both Sources and Targets can belong to groups. A group is an identifier that indicates which items can interact with each other. A group can be a single string or an array of strings. Items belonging to the same groups can interact. The following rules apply:
If neither source nor target has groups, the drag is valid.
If the source and the target have groups and the groups intersect, the drag is valid.
If the source and the target have groups but no intersection, the drag is invalid.
If the source has groups but the target does not, the drag is invalid.
If the source does not have groups but the target does, the drag is invalid.
Below are some example source group configurations:
// Can only be dropped on targets with no groups
{}
// Can only be dropped on targets that contain group1
{ groups: 'group1' }
// Can be dropped on targets that contain group1 or group2
{ groups: ['group1', 'group2'] }
Target has a configurable method accepts
which is called when a Source first enters the target. This allows the
Target to determine on a per source basis whether it will interact with it. The accepts
method is passed the info
object and should return a boolean indicating whether it will accept the source. Note that the getData
method
cannot be called here, however any properties on the info object may be accessed.
// Interrogating types
{
accepts: function(info) {
return info.types.indexOf('userRecord') > -1;
}
}
// Accessing property data
new Ext.drag.Source({
describe: function(info) {
info.total = 100;
}
});
new Ext.drag.Target({
accepts: function(info) {
return info.total > 75;
}
});
When using the drag namespace in conjunction with components, it is often useful to wrap the parts in plugins or other containers to provide a better contextual API. The following code sample implements the ability to drag rows in the Modern Grid.
The code doesn't handle any of the mechanics of doing the drag/drop, however it does provide a better API for using with components, as well as filling in some of the lower level detail needed to make the drag function.
In this example, we'll integrate a custom Drag and Drop plugin with a Grid. We'll then disable drag for anything with a record name of "Bar".
Ext.define('RowDragger', {
extend: 'Ext.AbstractPlugin',
alias: 'plugin.rowdrag',
mixins: ['Ext.mixin.Observable'],
config: {
recordType: ''
},
constructor: function(config) {
this.mixins.observable.constructor.call(this, config);
},
init: function(component) {
var me = this,
type = this.getRecordType();
this.source = new Ext.drag.Source({
element: component.element,
delegate: '.x-grid-row',
describe: function(info) {
var row = Ext.Component.fromElement(info.eventTarget, component, 'gridrow');
info.record = row.getRecord();
},
proxy: {
type: 'placeholder',
getElement: function(info) {
var el = this.element;
if (!el) {
this.element = el = Ext.getBody().createChild({
style: 'padding: 10px; width: 100px; border: 1px solid gray; color: red;',
});
}
el.show().update(info.record.get('name'));
return el;
}
},
autoDestroy: false,
listeners: {
scope: me,
beforedragstart: this.makeRelayer('beforedragstart'),
dragstart: this.makeRelayer('dragstart'),
dragmove: this.makeRelayer('dragmove'),
dragend: this.makeRelayer('dragend')
}
});
},
disable: function() {
this.source.disable();
},
enable: function() {
this.source.enable();
},
doDestroy: function() {
Ext.destroy(this.source);
this.callParent();
},
makeRelayer: function(name) {
var me = this;
return function(source, info) {
return me.fireEvent(name, me, info);
};
}
});
Ext.define('User', {
extend: 'Ext.data.Model',
fields: ['name']
});
Ext.Viewport.add({
xtype: 'grid',
store: {
model: 'User',
data: [{
id: 1,
name: 'Foo'
}, {
name: 'Bar'
}, {
name: 'Baz'
}]
},
columns: [{
dataIndex: 'name',
text: 'Name',
flex: 1
}],
plugins: [{
type: 'rowdrag',
recordType: 'user',
listeners: {
beforedragstart: function(plugin, info) {
return info.record.get('name') !== 'Bar';
}
}
}]
});
As Ext JS continues to evolve, the Ext.drag API will be incorporated in plugins like the example above and more. Today, Ext.drag offers a convenient way to handle drag-and-drop for both Toolkits and plays nicely with the standard HTML5 API.