Hybrid View

  1. #1
    Sencha Premium Member
    Join Date
    Jan 2010
    Location
    Vancouver, Canada
    Posts
    136
    Vote Rating
    26
    bogc will become famous soon enough bogc will become famous soon enough

      0  

    Default What is the best pattern/practice for saving nested data in ExtJs 4?

    What is the best pattern/practice for saving nested data in ExtJs 4?


    This question is related to my other posts:

    http://www.sencha.com/forum/showthre...-a-json-object

    http://www.sencha.com/forum/showthre...hildren-change

    I am going to use a simple example: Order & Line Items defined as two models, with Order having one or more Line Items.

    The main requirement is this: when I save the Order and its Line Items, they have to be saved as one transaction.

    In ExtJs 3 I would define two stores, one for the Order and one for Line Items, I would do an ajax call passing the data from both to a server controller method, and then, on the server side (I use Ext.Net), I would save them in the db using one transaction. This pattern works in ExtJs 4 as well.


    Now, in ExtJs 4 we have models that are capable of having relationships :-) so, instead of two stores, I have one store with a HasMany relationship, an Order has many Line Items.

    I sort of expected that I can save an Order, and when I save the order, the data from its Line Items models is included as well. That is not what happens from what I've seen debugging store.sync() calls.

    One of the things that is not clear to me is how the proxy api calls work as individual http requests in the context where the requirement is that all the row changes (including changes in their associated data) have to be saved as one unit. I guess one could implement the server request handlers to temporarily store the data on the server side, and then when the batch completes, one more call has to be made to persist the data as one unit in a database.

    Another idea I tried is to get all the data from the store (including child data) and resolve all the changes on the server side (it is better to do it there anyway just because the data on the client could become stale sooner). There seems to be no extjs method that extracts the store data and follows the HasMany relationships. Something like Writer.getRecordData() that would follow the nested data.

    And, as I said, another problem that I see is that, when the child changes, the parent is not marked as dirty, and even if it did, the Writer doesn't seem to include the children in the request that is sent to the server.

    Any ideas, comments?

    Thanks

    Update: Ext.Net has implemented an nice Store API method, getChangedData, that extracts all the changes from a store (not including the nested data though). This allows saving all the row changes in a store (again, not including the children) in one server call.

  2. #2
    Sencha - Support Team scottmartin's Avatar
    Join Date
    Jul 2010
    Location
    Houston, Tx
    Posts
    9,154
    Vote Rating
    475
    scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future scottmartin has a brilliant future

      0  

    Default


    The writer does not support nested data. You will have to override this yourself or send the data object yourself and update at the server.

    Scott.

  3. #3
    Sencha Premium Member
    Join Date
    Jan 2010
    Location
    Vancouver, Canada
    Posts
    136
    Vote Rating
    26
    bogc will become famous soon enough bogc will become famous soon enough

      0  

    Default


    I ended up using an Ext.Net specific addition to the API and the getAssociatedData function.

  4. #4
    Sencha Premium Member
    Join Date
    Nov 2012
    Location
    Bangalore
    Posts
    79
    Vote Rating
    1
    rupamkhaitan is on a distinguished road

      0  

    Default sample code

    sample code


    hi bogc

    Can you paste some sample code

  5. #5
    Sencha User
    Join Date
    Feb 2013
    Location
    Lawrence, KS, USA
    Posts
    25
    Vote Rating
    0
    jasewell is on a distinguished road

      0  

    Default A simple workaround

    A simple workaround


    The workaround I eventually settled on is simpler (with all the good and bad that implies) than the other solutions I've seen to this problem on the forums. Simply put: the parent model has an unused field of type 'auto' with the same name as its associationKey, and there's a beforesync listener on the store that assigns the data from all its dependent records to that field. I also overrode the set() method on the child model to set the parent model dirty if appropriate.

    If my data had more than one level of nesting I'd probably scrap this approach in favor of extending Writer, but this was faster for this project.
    Code:
    Ext.define('ClientTestDetail', {
    	extend: 'Ext.data.Model',
    	idProperty: 'client_test_details_id',
    	fields: [
    		{ name: 'client_test_details_id', type: 'int' },
    		/* ... */
    		{ name: 'year_info' } // Unused field to hold dependent record data.
    	],
    	hasMany: [{ model: 'ClientTestYearInfo', name: 'yearInfo', associationKey: 'year_info' }]
    });
    
    
    Ext.define('ClientTestYearInfo', {
    	extend: 'Ext.data.Model',
    	idProperty: 'end_year_int',
    	fields: [
    		{ name: 'end_year_int',           type: 'int' },
    		/* ... */
    	],
    	set: function(fieldName, newValue) {
    		this.callParent(arguments);
    		if (this.dirty && this.ClientTestDetailBelongsToInstance)
    			this.ClientTestDetailBelongsToInstance.setDirty();
    	},
    	belongsTo: 'ClientTestDetail'
    });
    
    
    Ext.create('Ext.data.Store', {
    	model: 'ClientTestDetail',
    	pageSize: 50,
    	listeners: {
    		beforesync: function(syncCandidates) {
    			var writerBound = Ext.Array.merge(syncCandidates.update || [], syncCandidates.create || []);
    			Ext.each(writerBound, function(rec) {
    				rec.data.year_info = Ext.Array.pluck(rec.yearInfo().data.items, 'data');
    			});
    		}
    	},
    	proxy: {
    		type: 'ajax',
    		api: {
    			create:  '../api/client_details/test_details/insert',
    			read:    '../api/client_details/test_details/view',
    			update:  '../api/client_details/test_details/update',
    			destroy: '../api/client_details/test_details/delete'
    		},
    		reader: {
    			type: 'json',
    			root: 'rows'
    		},
    		writer: {
    			type: 'json',
    			root: 'records',
    			allowSingle: false,
    			encode: true
    		}
    	}
    });

  6. #6
    Sencha User
    Join Date
    Apr 2013
    Posts
    4
    Vote Rating
    3
    wgrant is on a distinguished road

      2  

    Default Thumbs Down

    Thumbs Down


    This is a surprising oversight for an 'Enterprise' level JavaScript library. Wouldn't it be standard practice to send the whole object to the back end and let the database handle the update in a transaction safe manner?