1. #1
    Sencha User kitesurf's Avatar
    Join Date
    Jan 2012
    Location
    London, England
    Posts
    73
    Answers
    4
    Vote Rating
    2
    kitesurf is on a distinguished road

      0  

    Default Unanswered: AJAX Store & loading data from nested hasMany association

    Unanswered: AJAX Store & loading data from nested hasMany association


    I have a store and model called 'availability'. On the availability model I have a hasMany association to balanceItems. This in turn also has a hasMany association to 'movements'.

    When I load the AJAX availability for the first time, I get all the data I expect, all good.

    However, if the back-end data changes and I do another availability store load(), the data in the hasMany associations is not updated.

    Actually, that's not strictly true, it depends how I access the data that is the cause for difference.

    In debug I wait for the callback to return from the AJAX call and I then inspect the store object after getting the store using the storemgr:

    Code:
    Ext.StoreMgr.get("AvailabilityStore")
    The hasMany association I am interested in is 'movements'. As far as I can make out, Sencha automatically creates a 'store' for this hasMany assocation called 'movementsStore'.

    If I look at the store data item in debug using the following path, I still have old data from when the store was first loaded:
    Code:
    availabilityStore.getAt(0).balanceItemsStore.data.all[0].data.movements[3]
    
    • balance: 139186
    • balanceitemsmodel_id: "1010-0100"
    • creditAmount: null
    • date: Thu Feb 02 2012 00:00:00 GMT+0000 (GMT)
    • debitAmount: 5250
    • description: "Invoice x 6"
    • formattedBalance: "139,186.00"
    • formattedCreditAmount: " "
    • formattedDate: "2 Feb 2012"
    • formattedDebitAmount: "5,250.00"
    • id: "ext-record-9"
    • xindex: 4
    • __proto__: Object
    If however I access it down the .raw route, I can see the updated data:
    Code:
    availabilityStore.getAt(0).raw.balanceItems[0].movements
    
    • balance: 139419
    • creditAmount: null
    • date: 1328140800000
    • debitAmount: 5483
    • description: "Invoice x 7"
    • __proto__: Object
    What I don't understand is why is the data not the same? Do I have to do some kind of sync on the automatically created hasMany stores that Sencha has created?

    I have included a copy of the full store object below as well as my model definitions. Hopefully someone can spot where I am going wrong or correct my interpretation of how to do this?

    Many thanks!
    • _data: Object
    • balanceItemsStore: Ext.apply.create.f
      • _data: Ext.apply.create.f
      • _filters: Array[1]
      • _model: function (){return this.constructor.apply(this,arguments)}
      • _modelDefaults: Ext.Object.classify.c
      • _proxy: Ext.apply.create.f
      • _remoteFilter: true
      • _remoteSort: false
      • _storeId: "ext-data-store-41"
      • _syncRemovedRecords: false
      • config: Ext.Object.classify.c
      • data: Ext.apply.create.f
        • _autoFilter: false
        • _autoSort: true
        • _filterRoot: "data"
        • _filters: Ext.apply.create.f
        • _sortRoot: "data"
        • all: Array[7]
          • 0: Ext.apply.create.f
            • _data: Object
            • data: Object
            • dirty: true
            • editing: false
            • id: "ext-record-10"
            • internalId: "1010-0100"
            • modified: Object
            • movementsStore: Ext.apply.create.f
              • _data: Ext.apply.create.f
              • _filters: Array[1]
              • _model: function (){return this.constructor.apply(this,arguments)}
              • _modelDefaults: Ext.Object.classify.c
              • _proxy: Ext.apply.create.f
              • _remoteFilter: true
              • _remoteSort: false
              • _storeId: "ext-data-store-2"
              • _syncRemovedRecords: false
              • config: Ext.Object.classify.c
              • data: Ext.apply.create.f
                • _autoFilter: false
                • _autoSort: true
                • _filterRoot: "data"
                • _filters: Ext.apply.create.f
                • _sortRoot: "data"
                • all: Array[4]
                  • 0: Ext.apply.create.f
                  • 1: Ext.apply.create.f
                  • 2: Ext.apply.create.f
                  • 3: Ext.apply.create.f
                    • _data: Object
                    • data: Object
                      • balance: 139186
                      • balanceitemsmodel_id: "1010-0100"
                      • creditAmount: null
                      • date: Thu Feb 02 2012 00:00:00 GMT+0000 (GMT)
                      • debitAmount: 5250
                      • description: "Invoice x 6"
                      • formattedBalance: "139,186.00"
                      • formattedCreditAmount: " "
                      • formattedDate: "2 Feb 2012"
                      • formattedDebitAmount: "5,250.00"
                      • id: "ext-record-9"
                      • xindex: 4
                      • __proto__: Object
                    • dirty: true
                    • editing: false
                    • id: "ext-record-9"
                    • internalId: "ext-record-9"
                    • modified: Object
                    • phantom: true
                    • raw: Object
                    • stores: Array[2]
                    • __proto__: a
                  • length: 4
                  • __proto__: Array[0]
                • config: Ext.Object.classify.c
                • dirtyFilterFn: true
                • dirtyIndices: false
                • filtered: true
                • getKey: function (a){return a.getId()}
                • indices: Object
                • initConfig: function (){}
                • initialConfig: Object
                • items: Array[4]
                • keys: Array[4]
                • length: 4
                • map: Object
                • __proto__: a
              • eventDispatcher: Ext.apply.create.f
              • getEventDispatcher: function (){return this.eventDispatcher}
              • getId: function (){return this.id}
              • getObservableId: function (){return this.observableId}
              • getUniqueId: function (){return this.id}
              • id: "ext-data-store-2"
              • initConfig: function (){}
              • initialConfig: Object
              • managedListeners: Object
              • observableId: "#ext-data-store-2"
              • removed: Array[0]
              • usedSelectors: Array[1]
              • __proto__: a
            • raw: Object
            • stores: Array[12]
            • __proto__: a
          • 1: Ext.apply.create.f
          • 2: Ext.apply.create.f
          • 3: Ext.apply.create.f
          • 4: Ext.apply.create.f
          • 5: Ext.apply.create.f
          • 6: Ext.apply.create.f
          • length: 7
          • __proto__: Array[0]
        • config: Ext.Object.classify.c
        • dirtyFilterFn: true
        • dirtyIndices: true
        • filtered: true
        • getKey: function (a){return a.getId()}
        • indices: Object
        • initConfig: function (){}
        • initialConfig: Object
        • items: Array[7]
        • keys: Array[7]
        • length: 7
        • map: Object
        • __proto__: a
      • eventDispatcher: Ext.apply.create.f
      • getEventDispatcher: function (){return this.eventDispatcher}
      • getId: function (){return this.id}
      • getObservableId: function (){return this.observableId}
      • getUniqueId: function (){return this.id}
      • id: "ext-data-store-41"
      • initConfig: function (){}
      • initialConfig: Object
      • managedListeners: Object
      • observableId: "#ext-data-store-41"
      • removed: Array[0]
      • usedSelectors: Array[1]
      • __proto__: a
    • data: Object
    • id: "ext-record-43"
    • internalId: "ext-record-43"
    • modified: Object
    • paymentMethodsStore: Ext.apply.create.f
    • paymentRecipientsStore: Ext.apply.create.f
    • phantom: true
    • raw: Object
      • asAtDate: 1350246360000
      • balanceItems: Array[8]
        • 0: Object
          • amount: 139419
          • hasMovements: true
          • id: "1010-0100"
          • label: "Sales Ledger"
          • maxPaymentRequest: false
          • movements: Array[4]
            • 0: Object
            • 1: Object
            • 2: Object
            • 3: Object
              • balance: 139419
              • creditAmount: null
              • date: 1328140800000
              • debitAmount: 5483
              • description: "Invoice x 7"
              • __proto__: Object
            • length: 4
            • __proto__: Array[0]
          • pendingPayments: false
          • __proto__: Object
        • 1: Object
        • 2: Object
        • 3: Object
        • 4: Object
        • 5: Object
        • 6: Object
        • 7: Object
        • length: 8
        • __proto__: Array[0]
      • paymentMethods: Array[1]
      • paymentRecipients: Array[1]
      • __proto__: Object
    • stores: Array[1]
    • __proto__: a
    Code:
    Ext.define('Aries.model.AvailabilityModel', {
        extend: 'Ext.data.Model',
    
        uses: [
            'Aries.model.BalanceItemsModel',
            'Aries.model.PaymentMethodModel',
            'Aries.model.PaymentRecipientModel'
        ],
    
        config: {
            fields: [
                {
                    name: 'asAtDate',
                    dateFormat: 'time',
                    type: 'date'
                }
            ],
            hasMany: [
                {
                    associationKey: 'balanceItems',
                    model: 'Aries.model.BalanceItemsModel',
                    name: 'balanceItems'
                },
                {
                    associationKey: 'paymentMethods',
                    model: 'Aries.model.PaymentMethodModel',
                    name: 'paymentMethods'
                },
                {
                    associationKey: 'paymentRecipients',
                    model: 'Aries.model.PaymentRecipientModel',
                    name: 'paymentRecipients'
                }
            ]
        }
    });
    Code:
    Ext.define('Aries.model.BalanceItemsModel', {
        extend: 'Ext.data.Model',
        alias: 'model.BalanceItemsModel',
    
        uses: [
            'Aries.model.NewMovementModel'
        ],
    
        config: {
            fields: [
                {
                    name: 'id',
                    type: 'string'
                },
                {
                    name: 'label',
                    type: 'string'
                },
                {
                    name: 'amount',
                    type: 'float'
                },
                {
                    name: 'pendingPayments',
                    type: 'boolean'
                },
                {
                    name: 'maxPaymentRequest',
                    type: 'boolean'
                },
                {
                    name: 'hasMovements',
                    type: 'boolean'
                }
            ],
            hasMany: {
                associationKey: 'movements',
                model: 'Aries.model.NewMovementModel',
                name: 'movements'
            }
        }
    });
    Code:
    Ext.define('Aries.model.NewMovementModel', {
        extend: 'Ext.data.Model',
    
        config: {
            fields: [
                {
                    name: 'date',
                    dateFormat: 'time',
                    type: 'date'
                },
                {
                    name: 'formattedDate'
                },
                {
                    name: 'description'
                },
                {
                    name: 'creditAmount',
                    type: 'float'
                },
                {
                    name: 'formattedCreditAmount'
                },
                {
                    name: 'debitAmount',
                    type: 'float'
                },
                {
                    name: 'formattedDebitAmount'
                },
                {
                    name: 'balance',
                    type: 'float'
                },
                {
                    name: 'formattedBalance'
                }
            ]
        }
    });

  2. #2
    Sencha - Senior Forum Manager mitchellsimoens's Avatar
    Join Date
    Mar 2007
    Location
    Gainesville, FL
    Posts
    37,206
    Answers
    3517
    Vote Rating
    856
    mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute mitchellsimoens has a reputation beyond repute

      0  

    Default


    It updates for me using 2.1.0 RC2

    Code:
    Ext.define('Product', {
        extend : 'Ext.data.Model',
        config : {
            fields : [
                {name : 'id', type : 'int'},
                {name : 'user_id', type : 'int'},
                {name : 'name', type : 'string'}
            ]
        }
    });
    
    Ext.define('User', {
        extend : 'Ext.data.Model',
        config : {
            fields  : [
                {name : 'id', type : 'int'},
                {name : 'name', type : 'string'}
            ],
            // we can use the hasMany shortcut on the model to create a hasMany association
            hasMany : {model : 'Product', name : 'products'}
        }
    });
    
    Ext.application({
        name   : 'Test',
    
        launch : function () {
    
            new Ext.data.Store({
                autoLoad  : true,
                model     : 'User',
                proxy     : {
                    type : 'ajax',
                    url  : 'data/json.json'
                },
                listeners : {
                    single : true,
                    load   : function(store, recs) {
                        var rec      = recs[0],
                            products = rec.products();
            
                        console.log(products.getAt(0).get('name'));
            
                        setTimeout(function() {
                            store.load(function(recs) {
                                var rec      = recs[0],
                                    products = rec.products();
            
                                console.log(products.getAt(0).get('name'));
                            });
                        }, 5000);
                    }
                }
            });
    
        }
    });
    This json at first:

    Code:
    [
        {
            "id"       : 1,
            "name"     : "Mitchell Simoens",
            "products" : [
                {
                    "id"      : 2,
                    "user_id" : 1,
                    "name"    : "Apple"
                }
            ]
        }
    ]
    And I just changed Apple to Apples within 5 seconds and it consoles out Apple then Apples
    Mitchell Simoens @SenchaMitch
    Sencha Inc, Senior Forum Manager
    ________________
    Check out my GitHub, lots of nice things for Ext JS 4 and Sencha Touch 2
    https://github.com/mitchellsimoens

    Think my support is good? Get more personalized support via a support subscription. https://www.sencha.com/store/

    Need more help with your app? Hire Sencha Services services@sencha.com

    Want to learn Sencha Touch 2? Check out Sencha Touch in Action that is in print!

    When posting code, please use BBCode's CODE tags.

  3. #3
    Sencha User kitesurf's Avatar
    Join Date
    Jan 2012
    Location
    London, England
    Posts
    73
    Answers
    4
    Vote Rating
    2
    kitesurf is on a distinguished road

      0  

    Default


    Hi Mitchell,

    Thank you for taking the time to check this out!

    I am using version 2.0.1.1, so this may well have been fixed in 2.1.0.

    I've attached an updated version of the test which includes a further hasMany on products called sizes.

    I've also registered a 'users' store and use this to get my data after getting the store from the store mgr. Perhaps I am not doing this correctly or missing something conceptually. Would you mind doing some sanity checking on my code and testing this in 2.1.0 to see what you get. I've put in a debugger statement so as to have time to change a few of the values in the json file.

    In my tests it doesn't show any changes in the json file. It always remains the same as the initial data load.

    thanks!

    Code:
    Ext.define('Sizes', {
        extend : 'Ext.data.Model',
        config : {
            fields : [
                {name : 'id', type : 'string'},
                {name : 'desc', type : 'string'}
            ]
        }
    });
    
    Ext.define('Product', {
        extend : 'Ext.data.Model',
        config : {
            fields : [
                {name : 'id', type : 'int'},
                {name : 'user_id', type : 'int'},
                {name : 'name', type : 'string'}
            ],
            hasMany : {model : 'Sizes', name : 'sizes'}
        }
    });
    
    Ext.define('User', {
        extend : 'Ext.data.Model',
        config : {
            fields  : [
                {name : 'id', type : 'int'},
                {name : 'name', type : 'string'}
            ],
            // we can use the hasMany shortcut on the model to create a hasMany association
            hasMany : {model : 'Product', name : 'products'}
        }
    });
    
    Ext.application({
        name   : 'Test',
    
        launch : function () {
    
            new Ext.data.Store({
                autoLoad  : true,
                model     : 'User',
                storeId   : 'Users',
                proxy     : {
                    type : 'ajax',
                    url  : 'data/json.json'
                },
                listeners : {
                    single : true,
                    load   : function(store, recs) {
                                var userStores = Ext.StoreMgr.get("Users");
                                var user = Ext.StoreMgr.get("Users").getAt(0);
                                var product = user.products().getAt(0);
                                var sizes = product.sizes().getAt(0);                            
                                console.log(user.get('name'));
                                console.log(product.get('name'));
                                console.log(sizes.get('desc'));
            
                        setTimeout(function() {
                            store.load(function(recs) {
                                debugger;
                                var userStores = Ext.StoreMgr.get("Users");
                                var user = Ext.StoreMgr.get("Users").getAt(0);
                                var product = user.products().getAt(0);
                                var sizes = product.sizes().getAt(0);                            
                                console.log(user.get('name'));
                                console.log(product.get('name'));
                                console.log(sizes.get('desc'));
                            });
                        }, 5000);
                    }
                }
            });
    
        }
    });
    Code:
    [
        {
            "id"       : 1,
            "name"     : "Mitchell Simoens",
            "products" : [
                {
                    "id"      : 2,
                    "user_id" : 1,
                    "name"    : "Apple",
                    "sizes"   : [
                        {
                            "id"    : "S",
                            "desc"    : "Small"
                        },
                        {
                            "id"    : "M",
                            "desc"    : "Medium"
                        }
                        
                    ] 
                }
            ]
        }
    ]

  4. #4
    Sencha User
    Join Date
    Apr 2012
    Posts
    6
    Vote Rating
    1
    sf_cs is on a distinguished road

      0  

    Default


    Hi,

    It seems this error still exists. I ran into the same problem.

    It only occurs when the hasMany store has less items than in the first call. If your related store has 3 items after the first load and 2 items after the second load the first two gets overwritten and number 3 will not be removed.

    I did not debug that much but I think the error is in

    Ext.data.Store - insert: function(index, records)

    or

    Ext.util.Collection - insertAll: function(index, insertItems)


    As a temporary fix you can delete the related store calling the removeAll method.

    Regards

Thread Participants: 2

Tags for this Thread