the singularity of being and nothingness
References in Ext JS 5
One of the most common tasks for a developer when developing an Ext JS application is dealing with the complexity of nested components. For example, let’s say that you have a simple Ext.panel.Form definition that resembles something like this:
- Form Panel
- Toolbar
- Save Button
- Cancel Button
- Field Set
- Field Container
- Field 1
- Field 2
- Field Container
- Field 3
- Field 4
- Field Container
- Field Set
- Field Container
- Field 5
- Field Container
- Toolbar
Leaving aside all that Ext JS does behind the scenes to simplify management of forms in general, we see that even a simple form definition like this has a fairly complex structure.
Now to the task at hand: given this complex structure, how do we go about retrieving a given component from within this hierarchy? For example, let’s say that we want to specifically target Field 5. How do we do it?
By Id
One approach that was used in the past was to manually assign an id to the component and use Ext.getCmp() to retrieve it.
// field definition ... { xtype: 'textfield', id: 'myfield' } // let's get it... var field = Ext.getCmp( 'myfield' );
The really nice part of this is that it’s fast. The downside, however, can be pretty significant. Since the id has to be unique, you not only have to manually assign unique ids to your lookup-able components (imagine doing this in a huge, complex application!), but you can easily run into errors when trying to create multiple instances of the same component…precisely because of the unique id collision.
NOTE: Ext.getCmp() still works, and works really well. However, it is considered best practices to avoid using it unless you absolutely need to or can confidently guarantee the uniqueness of the id.
Component Query
Another approach that is very common in Ext JS 4 applications is a component query (via refs, traversal methods like down(), up(), etc.). Consider this example:
// form definition { xtype: 'form', title: 'My Form', items: [ { xtype: 'fieldset', title: 'Personal Info', itemId: 'personalInfo' ... } ] } // get the fieldset var fieldset = myform.down( '#personalInfo' );
This approach is nice because itemIds are scoped to the instance of their container, so having multiple components with the same itemId will not cause errors like duplicate ids would. Another big benefit is that this approach is super-flexible. Using component queries, you can match on any valid selector–itemId, xtype, and much more–which can come in very handy.
The downside, of course, is performance. When using down() in our example, the descendant components of myForm will be traversed until a component matching our selector (an itemId of “personalInfo”) is found. In fairness, our example will still be very fast. However, such traversal does come with a cost and should be kept in mind when considering the best way to approach certain tasks.
References
Ext JS 5 introduces a new way of dealing with these scenarios. The reference config instructs the component to register itself with its owning view. If you’re using a ViewController, this is done automatically. Otherwise, you’ll want to assign a referenceHolder config to the owning view.
// form definition { xtype: 'form', referenceHolder: true, title: 'My Form', items: [ { xtype: 'fieldset', title: 'Personal Info', reference: 'personalInfo' } ] }
Now that we’ve assigned the reference, getting our fieldset is as simple as using the lookupReference() method
// get the fieldset by reference var fieldset = myForm.lookupReference( 'personalInfo' )
On the face of it, this looks very similar to the query approach we looked at before. In the background, however, it’s quite a bit different and more efficient. As discussed above, the query method requires the descendants of myForm to be traversed until a component matching the selector is found. WIth references, no search is needed since the registration cache is simply consulted and the reference is returned.
More About referenceHolder
As mentioned above, you can set the referenceHolder config to specify that the container will be the point in the hierarchy where references to items with the reference config will be held (hence, “referenceHolder”). An important thing to keep in mind, however, is that there can be multiple reference holders configured within a hierarchy. Consider these two examples:
Ext.define('MyForm', { extend: 'Ext.form.Panel', referenceHolder: true, items: [ { xtype: 'fieldcontainer', reference: 'name', items: [ { xtype: 'textfield', name: 'firstName', reference: 'firstName', fieldLabel:'First Name' }, { xtype: 'textfield', name: 'lastName', reference: 'lastName', fieldLabel: 'Last Name' } ] }, { xtype: 'fieldcontainer', reference: 'personal', items: [ { xtype: 'datefield', name: 'DOB', reference: 'DOB', fieldLabel: 'Date of Birth' }, { xtype: 'textfield', name: 'email', reference: 'email', fieldLabel: 'Email' } ] } ] });
In this first example, we have a single referenceHolder at the top of the hierarchy. If we were to use getReferences() to retrieve all references for this view, we’d get 6: “name, firstName, lastName, personal, DOB, email”.
Ext.define('MyForm', { extend: 'Ext.form.Panel', referenceHolder: true, items: [ { xtype: 'fieldcontainer', reference: 'name', referenceHolder: true, items: [ { xtype: 'textfield', name: 'firstName', reference: 'firstName', fieldLabel:'First Name' }, { xtype: 'textfield', name: 'lastName', reference: 'lastName', fieldLabel: 'Last Name' } ] }, { xtype: 'fieldcontainer', reference: 'personal', referenceHolder: true, items: [ { xtype: 'datefield', name: 'DOB', reference: 'DOB', fieldLabel: 'Date of Birth' }, { xtype: 'textfield', name: 'email', reference: 'email', fieldLabel: 'Email' } ] } ] });
This example is pretty much the same, with two important differences. Instead of having a single referenceHolder at the top of our hierarchy, we have two additional reference holders configured. If we were to do getReferences() again from the top of our hierarchy, instead of 6 references, we’ll only get two: ‘name’ and ‘personal’.
This happens because reference holders are encapsulated. Any references in the hierarchy will be “held” by the closest reference holder ancestor. In our example, then, the “name” and “personal” references are themselves reference holders, and so we will need to retrieve their descendant references by calling lookupReference() on their ancestor reference holder:
// try to get "DOB" reference myForm.lookupReference( 'DOB' ); // won't work, because of reference holder encapsulation // get "name" reference holder as reference from form var nameFieldset = myForm.lookupReference( 'name' ); // try to get "DOB" reference nameFieldset.lookupReference( 'DOB' ); //works because "name" is referenceHolder of DOB
NOTE: The use of getReferences() above was for demonstration purposes only. Please see the important notes about the usage of this method in the documentation.
Conclusion
As we’ve seen, the addition of references within Ext JS 5 provide a powerful (and fast!) new approach to managing complex component hierarchies. I hope this has been a useful introduction to the concept. Thoughts or questions on using references? Leave a comment!
Thanks!
Print article | This entry was posted by existdissolve on November 15, 2014 at 1:00 pm, and is filed under Ext JS 5. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |
about 9 years ago
In your last example, you have it mixed up. You can have EITHER
// get “name” reference holder as reference from form
var nameFieldset = myForm.lookupReference( ‘name’ );
// try to get “lastName” reference
var lastNameField = nameFieldset.lookupReference( ‘lastName’ ); //works because “name” is referenceHolder of lastName
OR
// get “personal” reference holder as reference from form
var personalFieldset = myForm.lookupReference( ‘personal’ );
// try to get “DOB” reference
var dobField = personalFieldset.lookupReference( ‘DOB’ ); //works because “personal” is referenceHolder of DOB