Skip to content

Modus-Logo-Long-BlackCreated with Sketch.

  • Services
  • Work
  • Blog
  • Resources

    OUR RESOURCES

    Innovation Podcast

    Explore transformative innovation with industry leaders.

    Guides & Playbooks

    Implement leading digital innovation with our strategic guides.

    Practical guide to building an effective AI strategy
  • Who we are

    Our story

    Learn about our values, vision, and commitment to client success.

    Open Source

    Discover how we contribute to and benefit from the global open source ecosystem.

    Careers

    Join our dynamic team and shape the future of digital transformation.

    How we built our unique culture
  • Let's talk
  • EN
  • FR

Associations have undergone quite a few changes in Ext JS 5. Sencha has really put work into making them more functional and easier to use. Like all things though, they have their ups and downs. So, let’s dig into the new associations starting with…

The Good

Music Associations? Yeah, it’s very good. It’s very good for the digestion.

The New Ext.data.Field.reference Approach
One of the nice things Sencha has done is to eliminate the need to specify both sides of the association “equation” by giving us a new way to declare them. We do this by using the reference property of the field. So, if you have a Customer model with an Address association, you can simply have the following:

Ext.define('Customer', {
    extend : 'Ext.data.Model',
    fields : [
        'name'
    ]
});

Ext.define('Address', {
    extend : 'Ext.data.Model',
    fields : [
        { name : 'address'},
        { name : 'customerId', reference : 'Customer'}
    ]
});

Ext.define('Customers', {
    extend   : 'Ext.data.Store',
    model    : 'Customer',
    autoLoad : true,
    proxy : {
        type : 'ajax',
        url  : 'customers.json'
    }
});

// customers.json
[{
    id   : 1,
    name : "Modus Create",
    addresses : [{
           id      : 1,
           address : 'Reston, VA'
    },{
        id      : 2,
        address : 'Providence, RI'
    }]
}]

The Extras
This will create an association and inverse association for both Customer and Address and should get our data tied together easily. If you inspect the Customers store, you’ll see that each record now has an addresses() getter that will return a store of this customer’s addresses. Also, each address record in that store will have a reference to the customer record and a “customerId” on its data property.

The Bad

How’s your digestion now?

Also the New Ext.data.Field.reference Approach

As I mentioned, the new method for defining these relationships is all driven from the reference property of a field in the association model. As of today, you can’t use the Ext.data.Field.reference form of the relationship from the parent (customer) model. This is unfortunate as most people will naturally approach these definitions from the right/parent/customer side. I know what you’re thinking. Tim is great. If you have 10 different models that all need address associations, then yes, you are going to need 10 address models. The number of model classes you have is going to increase… possibly by quite a bit. You could, of course, create a base address model (minus the reference field) and then extend all of the others from this base class.

Legacy HasMany
What if you don’t want forty new models or what if you already have a code base that is using hasMany associations? The answer is that you cannot use the new reference association feature but you can still create association relationships from the parent by using the hasMany config property on your parent model, just like you have been. Building on our example above, we can add a Vendors model with an Address hasMany.

Ext.define('Vendor', {
    extend: 'Ext.data.Model',
    requires: ['Address'],
    fields: [
        'name'
    ],    
    hasMany: [{
        name: 'addresses',
        // associationKey: 'foo',
        model: 'Address'
    }]
});

Fiddle: https://fiddle.sencha.com/#fiddle/914

Gotchas
If you run the fiddle above and check out the vendorsStore, you’ll see that we still get a reference to the appropriate parent model on each of the address models. One gotcha with this approach is that you still need to include the association model classname in your requires array. If you leave that out, your association data won’t load and you won’t even get a console error. So, don’t forget to put that in.

It’s also worth noting that “associationKey” is still available as a config option for hasMany associations. Unfortunately, the documentation for the hasMany association config is completely gone. You can always refer to the Ext JS 4 docs until/if this gets added to the 5.0 docs.

Finally, under the covers, Ext JS 5 will actually run your hasMany associations through Ext.data.schema.Schema.addLegacyHasMany(). So, if you are curious, you can set a breakpoint at the end of that function and inspect the association it creates. You could then use that information to convert your hasManys over to field references if you wanted to.

The Ugly

Who the hell is that? One bastard association goes in, another one comes out.

Overrides
As hard as framework authors try, there is just no way they can predict or even accommodate everyone’s needs. We’ve had some requirements on our own projects that have needed some overrides. Remember, the danger with overrides is that, as the framework changes, your overrides may break. Be sure to pay close attention to your unit tests for these when you upgrade. You are doing unit testing right? OK, let’s look at these overrides in order.

1) Missing Association Data on Some Records

What if the server doesn’t send the nested association data on every record? Some servers won’t send empty objects in an attempt to keep transmission sizes down. For example, if you look at the fiddle again, you’ll notice in vendors.json that “Granite Parts” doesn’t have an address. What would normally happen is that the record with the missing data would not get an association store. We can change this with an override to Ext.data.schema.ManyToOne so that every record gets an association store even if its just an empty one.

/**
 * Association ManyToOne extension
 */
Ext.define('MyApp.data.schema.ManyToOne', {
  override: 'Ext.data.schema.ManyToOne',

  constructor: function(config) {
     this.Left.override({
        /**
         * The original function had a check to make sure that the read root for this
         * association existed in the data object. In our case, the server may not send
         * the association data if its empty. In that case, we want an empty store.
         */
        read: function(rightRecord, node, fromReader, readOptions) {
           var me = this,
               // We use the inverse role here since we're setting ourselves
               // on the other record
               key = me.inverse.role,

               // result = me.callParent([ rightRecord, node, fromReader, readOptions ]),
               result = me.callSuper([ rightRecord, node, fromReader, readOptions ]),
               store, leftRecords, len, i;
                
           // Did the root exist in the data?
           if (result.getReadRoot()) {

               // Create the store and dump the data
               store = rightRecord[me.getterName](null, null, result.getRecords());

               // Inline associations should *not* arrive on the "data" object:
               // delete rightRecord.data[me.role];
               delete rightRecord.data[me.associationKey ? me.associationKey : me.role];

               leftRecords = store.getData().items;

               for (i = 0, len = leftRecords.length; i < len; ++i) {
                   leftRecords[i][key] = rightRecord;
               }
                
           // Create an empty store if the root doesn't exist
           } else {
               store = rightRecord[me.getterName](null, null, []);
           }
        }
      });

      this.callParent(arguments);
   }
});

Fiddle: https://fiddle.sencha.com/#fiddle/914

2) Cleanup of “associationKey” Data
Association data for hasMany associations that uses an “associationKey” remains on the data object. Ext JS does a fine job of cleaning the data object of association data properties. By that I mean you won’t see an “address” property on any of the record data objects. It only falls down when you are using an “associationKey” on a legacy hasMany. So, you’ll notice up above, that we’ve replaced the delete rightRecord.data[me.role]; statement with one that supports association keys. If you are using dot notation in your associationKey then you’ll need to modify the code above to traverse the data object and delete the appropriate property.

3) Support for “storeConfig” on hasMany
hasMany config objects used to support a storeConfig property which was especially handy when you needed to supply your association store with sorters, etc. Let’s add support back in with an override to Ext.data.schema.Schema. Technically, the logic should go in the addLegacyHasMany method but we are going to put our custom code into the addReference method because it’s less invasive.

/**
 * Schema extension
 */
Ext.define('Schema', {
    override: 'Ext.data.schema.Schema',

    addReference: function (entityType, referenceField, descr, unique) {

        // add the storeConfig from the legacy hasMany association to the inverse
        if (descr.legacy && descr.inverse && descr.storeConfig && !descr.inverse.storeConfig) {
            descr.inverse.storeConfig = descr.storeConfig;
        }

        this.callParent(arguments);
    }
});

Fiddle: https://fiddle.sencha.com/#fiddle/914

Fin

With those overrides in place, we can now handle missing data from the server, clean up after association keys and sort our association stores. Now if we could only convince Sencha to support the Ext.data.Field.reference approach from the right/parent side…

Posted in Application Development
Share this

Timothy Eagan

Timothy Eagan was a Senior Engineer at Modus Create, and is an accomplished software developer with over 20 years of experience in creating customized business solutions. He can be found in Providence, RI. Tim is also an OIF veteran, former paperboy and part-time comedian.
Follow

Related Posts

  • Ext JS to React: FAQ
    Ext JS to React: FAQ

    This is part of the Ext JS to React blog series. React is Facebook's breakout…

  • React Landing
    Ext JS to React: Migration to Open Source

    Worried about Migrating from Ext JS? Modus has the Answers Idera’s acquisition of Sencha has…

Want more insights to fuel your innovation efforts?

Sign up to receive our monthly newsletter and exclusive content about digital transformation and product development.

What we do

Our services
AI and data
Product development
Design and UX
IT modernization
Platform and MLOps
Developer experience
Security

Our partners
Atlassian
AWS
GitHub
Other partners

Who we are

Our story
Careers
Open source

Our work

Our case studies

Our resources

Blog
Innovation podcast
Guides & playbooks

Connect with us

Get monthly insights on AI adoption

© 2025 Modus Create, LLC

Privacy PolicySitemap
Scroll To Top
  • Services
  • Work
  • Blog
  • Resources
    • Innovation Podcast
    • Guides & Playbooks
  • Who we are
    • Our story
    • Careers Old
  • Let’s talk
  • EN
  • FR