As you may already know, with the release of ExtJS 4.0 came a heralded new feature of associated data models, briefly introduced in the Countdown to Ext JS 4 Blog Post. At first glance, associations seem absolutely spectacular: an easy means of retrieving relational data without all the messy callbacks and recursive searching that you may have experienced in the past. Truth be told, associations are pretty cool, however there are just a few quirks that you must understand in order to use Ext associations without going crazy!
First and foremost, I highly recommend looking over the helpful tips documented over at Neil McGuigan’s ExtJS Tutorials Blog, particularly relevant to my post here are his posts covering Rules for hasMany Relationships as well as Rules for belongsTo and HasOne Associations.
The Good
ExtJS Associations are, once you get the hang of the, incredibly useful and a huge time saver! Don’t be discouraged if your associations do not immediately work, you’re very likely overlooking some moderately simple error. Again, referencing the tips in the aforementioned blog posts is always a good start when troubleshooting your finicky associations.
In my experience, I have found that it is almost always best to load associated data along with the parent model, this way you can use your associations immediately without having to concern yourself with any async calls to your application server. The data is readily available, as you expect it to be. For example, if you have an Order model with a hasMany relationship to an Order_Item model, you would expect that calling order.getItems(); should return the associated items, no? If you do not provide the associated data along with the Order data, this will not be the case!
The same kind of “gotcha” goes for saving and updating models with associated data (I’ll get into this a bit more later), you must provide the associated data in the application server’s response to your create/update request, otherwise the returned record will not have associations available. This can be particularly difficult to remember, especially if the associated data is there more so as a convenience to you as the developer rather than being explicitly required during the save or update operation.
For example, you may be creating a Shipment that belongsTo an Order (which is already created) therefore you may not think to send the associated order data back with the shipment data in the application server’s response to the create request. This of course will result in the ability to retrieve associated data in your success/failure callbacks on the model.save() operation.
The Bad
By default, a Proxy will not send associated data with the request. There are a few means of working around this, but personally I just add a property to the model and stick the associated data in there. For example, take a very simple set of related models; a Parent and a Child:
Ext.define("App.model.test.Parent",{ extend: 'Ext.data.Model', requires: ['App.model.test.Child'], fields: [ {name: 'id', type: 'int' }, {name: 'name', type: 'string'}, {name: 'kids', type: 'auto', defaultValue: []} // associated data is found here in the application server response! ], idProperty: 'id', hasMany: [{ foreignKey: 'parent_id', model: 'App.model.test.Child', associationKey: 'kids', name: 'getKids' }], proxy: { type: 'ajax', api : { create: '/service/test/create/format/json', read : '/service/test/read/format/json', update : '/service/test/update/format/json' }, reader: { idProperty : 'id', type : 'json', root : 'data', successProperty : 'success', messageProperty : 'message' }, writer: { type : 'json', writeAllFields : true } } }); Ext.define("App.model.test.Child",{ extend: 'Ext.data.Model', fields: [ {name: 'id', type: 'int' }, {name: 'name', type: 'string'}, {name: 'parent_id', type: 'int'} ] });
The application server’s response to a READ request for the Parent proxy may look something like this:
{ "data":{ "id":1, "name":"Homer Simpson", "children":{ "1":{ "id":1, "name":"Bart Simpson" }, "2":{ "id":1, "name":"Lisa Simpson" }, "3":{ "id":1, "name":"Maggie Simpson" } } }, "success":true, "message":null }
This ensures that associated data will be available immediately on load. Furthermore, whenever a Parent model needs to be saved, the associated Child model data should be added to the “kids” property so that it will be included in the request to the application server. Likewise, the all saved data should be included in the application server’s response to the create request.
The Ugly
The aforementioned trickery that is explicitly providing associated data on both ends of create/update requests can certainly lead to some confusion when associated data is not available when you strongly believe that it should be. This is compounded even further (in my opinion) by the behavior of the getRecords() method of an Ext.data.Operation, commonly used in the success/failure callbacks of model.save();
The documentation for Ext.data.Operation.getRecords() reads:
Returns the records associated with this operation. For read operations the records as set by the Proxy will be returned (returns null if the proxy has not yet set the records). For create, update, and destroy operations the operation’s initially configured records will be returned, although the proxy may modify these records’ data at some point after the operation is initialized.
The bolded section is what really got me. The getRecords() method will return the updated Record, but any associated data will not be updated, even if you provide the updated associated data in the application server response. Instead, the updated associated data can be found in the resultSet of the operation.
Conclusion
After going back and forth with using and not using the Association features in ExtJS 4.1, I have finally got everything working the way I need it to and I must say, I have a new found love for ExtJS’ Associations.
So long as you remember to always include associated data on both ends of the request (as a property on the model), and know where to retrieve the created/updated associated data in the response, Associations aren’t all the trouble that they are largely made out to be.
Cheers