Ext JS 4 introduces a new helper class called Ext.ComponentQuery
, which allows us to get references to Ext JS Components using CSS/XPath style selector syntax. This new class is very powerful and, as you will find out, is leveraged as an integral part of the MVC architecture system.
In this recipe we will demonstrate how to use the Ext.ComponentQuery
class to get references to specific components within a simple application. We will also move onto exploring how this query engine is integrated into the Ext.Container
class to make finding relative references very easy.
Finally we will look at adding our own custom selector logic to give us fine-grain control over the components that are retrieved.
We will start by creating a simple application, which consists of a simple Ext.panel.Panel
with a toolbar, buttons, a form, and a grid. This will form the basis of our examples as it has a number of components that we can query for.
var panel = Ext.create('Ext.panel.Panel', { height: 500, width: 500, renderTo: Ext.getBody(), layout: { type: 'vbox', align: 'stretch' }, items: [{ xtype: 'tabpanel', itemId: 'mainTabPanel', flex: 1, items: [{ xtype: 'panel', title: 'Users', id: 'usersPanel', layout: { type: 'vbox', align: 'stretch' }, tbar: [{ xtype: 'button', text: 'Edit', itemId: 'editButton' }], items: [{ xtype: 'form', border: 0, items: [{ xtype: 'textfield', fieldLabel: 'Name', allowBlank: false }, { xtype: 'textfield', fieldLabel: 'Email', allowBlank: false }], buttons: [{ xtype: 'button', text: 'Save', action: 'saveUser' }] }, { xtype: 'grid', flex: 1, border: 0, columns: [{ header: 'Name', dataIndex: 'Name', flex: 1 }, { header: 'Email', dataIndex: 'Email' }], store: Ext.create('Ext.data.Store', { fields: ['Name', 'Email'], data: [{ Name: 'Joe Bloggs', Email: '[email protected]' }, { Name: 'Jane Doe', Email: '[email protected]' }] }) }] }] }, { xtype: 'component', itemId: 'footerComponent', html: 'Footer Information', extraOptions: { option1: 'test', option2: 'test' }, height: 40 }] });
The main method of the Ext.ComponentQuery
class is the query
method. As we have mentioned, it accepts a CSS/XPath style selector string and returns an array of Ext.Component
(or subclasses of the Ext.Component
class) instances that match the specified selector.
Finding components based on xtype: We generally use a component's xtype as the basis for a selector and can retrieve references to every existing component of a xtype by passing it in to the query method. The following snippet will retrieve all
Ext.Panel
instances:var panels = Ext.ComponentQuery.query('panel');
Just like in CSS we can include the concept of nesting by adding a second xtype separated by a space. In the following example, we retrieve all the
Ext.Button
instances that are descendants of anExt.Panel
instance:var buttons = Ext.ComponentQuery.query('panel button');
Note
If you have custom classes whose xtypes include characters other than alphanumeric (for example, a dot or hypen) you cannot retrieve them in this way. You must instead query the
xtype
property of the components using the following syntax:var customXtypeComponents = Ext.ComponentQuery.query('[xtype=—My.Custom.Xtype—'];
Retrieving components based on attribute values: Along with retrieving references based on xtype, we can query the properties a component possesses to be more explicit about which components we want. In our sample application we have given the Save button an
action
property to distinguish it from other buttons. We can select this button by using the following syntax:var saveButton = Ext.ComponentQuery.query('button[action="saveUser"]');
This will return all
Ext.Button
instances that have anaction
property with a value ofsaveUser
.Combining selectors: It is possible to combine multiple selectors into one query in order to collect references to components that satisfy two different conditions. We do this by simply comma separating the selectors. The following code will select all
Ext.Button
andExt.form.field.Text
component instances:var buttonsAndTextfields = Ext.ComponentQuery.query('button, textfield');
Finding components based on ID: A component's
id
anditemId
can be included in a selector by prefixing it with the#
symbol. This syntax can be combined with all the others we have seen so far but IDs should be unique and so should not be necessary. The following code snippet will select a component with an ID ofusersPanel
:var usersPanel = Ext.ComponentQuery.query('#usersPanel');
Retrieving components based on attribute presence : One useful feature of the component query engine is that we can select components based on an attribute simply being present, regardless of its value. This can be used when we want to find components that have been configured with specific properties but don't know the values they might have. We can demonstrate this with the following code that will select all
Ext.Component
that have the propertyextraOptions
.var extraOptionsComponents = Ext.ComponentQuery.query('component[extraOptions]');
Using Components' Member Functions: It's also possible to execute a component's member function as a part of the selection criteria. If the function returns a truthy result then that component will be included (assuming the other criteria is met) in the result set. The following code shows this in action and will select all text fields who are direct children of a form and whose
isValid
method evaluates to true:var validField = Ext.ComponentQuery.query('form > textfield{isValid()}');
The Ext.ComponentQuery
is a singleton class that encapsulates the query logic used in our examples. We have used the query
method, which works by parsing each part of the selector and using it in conjunction with the Ext.ComponentManager
class. This class is responsible for keeping track of all the existing Ext.Component
instances, and is used to find any matching components.
There is one other method of the Ext.ComponentQuery
class to introduce and a further four methods that are part of the Ext.container.AbstractContainer
class.
The component query class allows us to evaluate a component reference we already have to find out if it matches a certain criteria. To do this we use the is
method, which accepts a selector identical to the ones that the query
method accepts and will return true if it does match. The following code determines if our main Ext.Panel
(referenced in the panel
variable) has an xtype of panel.
var isPanel = Ext.ComponentQuery.is(panel, 'panel');
There are four methods available in the Ext.container.AbstractContainer
class (which all container classes extend from; for example panels), which utilizes the component query engine and allow us to query using that component as the root. These methods are query
, child
, up
and down
. The query
method is identical to the query method available in the Ext.ComponentQuery
class but uses the container instance as the root of the query and so will only look for components under it in the hierarchy.
The up
and down
methods
retrieve the first component, at any level, either above or below the current component in the component hierarchy that matches the selector passed in.
Finally, the child
method retrieves the first direct child of the current instance that matches the selector.
Pseudo-selectors allow us to filter the retrieved result array based on some criteria that may be too complex to represent in a plain selector. There are two built-in pseudo-selectors: not
and last
. These can be added to a selector using a colon. The following example shows a selector that will retrieve the last text field.
var lastTextfield = Ext.ComponentQuery.query('textfield:last');
It is very simple for us to create our own custom pseudo-selectors; we will demonstrate how to add a pseudo-selector to retrieve components that are visible.
We start by creating a new function on the Ext.ComponentQuery.pseudos
object called visible
, which accepts one parameter that will contain the array of matches found so far. We will then add code to loop through each item, checking if it's visible and, if it is, adding it to a new filtered array. We then return this new filtered array.
Ext.ComponentQuery.pseudos.visible = function(items) { var result = []; for (var i = 0; i < items.length; i++) { if (items[i].isVisible()) { result.push(items[i]); } } return result; };
We can now use this in a selector in the same way as we did before. The following query will retrieve all visible components:
var visibleComponents = Ext.ComponentQuery.query('component:visible');
The recipes about MVC in Chapter 12, Advanced Ext JS for the Perfect App make use of component queries extensively.