1. #21
    Sencha User
    Join Date
    Jul 2011
    Posts
    11
    Vote Rating
    0
    hardwickj is on a distinguished road

      0  

    Default


    So in 4.1 RC1, a method Model.getData(boolean) was added. Passing a value of true will correctly call the getAssociatedData and return a complete object graph.

    However, upon further inspection of the Ext.data.writer.Writer.getRecordData() function, I see they are not making use of this but are instead only iterating over the non-association fields of the model object. I see no way of indicating to the Writer that it should utilize Model.getData(). I think as a default that this is the correct behavior since more often then not you do not want to return a complete object graph but instead persist the associated models independently. But we still need a means of passing an option such that we can indicate we DO want it to do this.

    I think this is an absolute necessity and I'm kind of shocked ExtJS has gone this far without something being done about this. It's things like this that make me desperately miss Backbone.js.

  2. #22
    Sencha User
    Join Date
    Jul 2011
    Posts
    11
    Vote Rating
    0
    hardwickj is on a distinguished road

      0  

    Default


    If anyone is interested, I solved this by making a custom Writer that is almost identical to the original Json writer but utilizes the new getData() method on Models for retrieving data to be written rather than just iterating over the fields. Then anytime you define a proxy on a model/store, if you wish for it to use this writer you just define it in the proxy config. See below.

    Defines the custom Writer:
    Code:
    Ext.define('Ext.data.writer.DeepJson', {
        extend:'Ext.data.writer.Json',
        getRecordData:function (record, operation) {
            var isPhantom = record.phantom === true,
                writeAll = this.writeAllFields || isPhantom,
                nameProperty = this.nameProperty,
                fields = record.fields,
                data = {},
                changes,
                name,
                field,
                key;
    
            if (writeAll) {
                // This is the branch that has been changed from the original Json Writer
                data = record.getData(true);
            } else {
                changes = record.getChanges();
                for (key in changes) {
                    if (changes.hasOwnProperty(key)) {
                        field = fields.get(key);
                        name = field[nameProperty] || field.name;
                        data[name] = changes[key];
                    }
                }
            }
            if (isPhantom) {
                if (operation && operation.records.length > 1) {
                    data[record.clientIdProperty] = record.internalId;
                }
            } else {
                data[record.idProperty] = record.getId();
            }
    
            return data;
        }
    });
    Then we add it to our proxy config:
    Code:
    proxy:{
            type:'rest',
            autoAbort:false,
            url:'api/graph',
            reader:{
                type:'json'
            },
            writer:Ext.create('Ext.data.writer.DeepJson')
        }
    Doing it this way you still maintain the default behavior of having to persist associations independently, but now have the option of utilizing an entire graph in the event you have something abnormally large where you may not want to submit 100's of requests to the back end.

  3. #23
    Sencha User olivierpons's Avatar
    Join Date
    Dec 2009
    Location
    Aix en Provence,France
    Posts
    116
    Vote Rating
    1
    Answers
    6
    olivierpons is on a distinguished road

      0  

    Default


    Trying your class, I get this error:

    Uncaught TypeError: Cannot read property 'persist' of undefined
    Ext.define.afterEdit
    - ext-all-debug.js:46945
    Ext.define.callStore
    - ext-all-debug.js:47942
    Ext.define.afterEdit
    - ext-all-debug.js:47917
    Ext.define.set
    - ext-all-debug.js:47590
    Ext.define.insert
    - ext-all-debug.js:49114
    Ext.define.add
    - ext-all-debug.js:49173
    Ext.define.onValidateClick - liste.js:547
    Ext.define.fireHandler
    - ext-all-debug.js:78194
    Ext.define.onClick
    - ext-all-debug.js:78184
    (anonymous function)
    Ext.apply.createListenerWrap.wrap
    - ext-all-debug.js:897

  4. #24
    Sencha User
    Join Date
    Jul 2011
    Posts
    11
    Vote Rating
    0
    hardwickj is on a distinguished road

      0  

    Default


    I'm not doing anything with "persist", I'm merely modifying which fields are collected for the request. A la, the following snippet from the original Ext.data.writer.Writer:

    Code:
    if (writeAll) {
                fLen = fieldItems.length;
    
                for (f = 0; f < fLen; f++) {
                    field = fieldItems[f];
    
                    if (field.persist) {
                        name       = field[nameProperty] || field.name;
                        data[name] = record.get(field.name);
                    }
                }
    } else {...
    has now become the following:
    Code:
    if (writeAll) {             
        data = record.getData(true);         
    } else {...
    If you are getting the above errors I'm going to guess that you have problems elsewhere or in how/where you are defining the new writer, but I can't help you without seeing any code of your own.

  5. #25
    Sencha User olivierpons's Avatar
    Join Date
    Dec 2009
    Location
    Aix en Provence,France
    Posts
    116
    Vote Rating
    1
    Answers
    6
    olivierpons is on a distinguished road

      0  

    Default


    After searching a while, the solutions chrisface and cstrong have provided didn't work with ExtJS and record insertion.

    So what I've done (and I think it's a bit safer) is calling first the parent's method, then after, add to the result the "hasMany" associations (if there are ones).

    Here's my code, which works flawlessly. Please feel free to comment or to suggest:

    Code:
    var ExtDataWriterJsonOriginal_getRecordData = Ext.data.writer.Json.prototype.getRecordData;
    Ext.data.writer.Json.override({
      {*/*
         * This function overrides the default implementation of
         * json writer. Any hasMany relationships will be submitted
         * as nested objects
         */*}
        getRecordData: function(record) {
            var me = this, i, association, childStore, data = {};
            data = ExtDataWriterJsonOriginal_getRecordData(record);
    
            /* Iterate over all the hasMany associations */
            for (i = 0; i < record.associations.length; i++) {
                association = record.associations.get(i);
                if (association.type == 'hasMany')  {
                    data[association.name] = [];
                    childStore = eval('record.'+association.name+'()');
    
                    //Iterate over all the children in the current association
                    childStore.each(function(childRecord) {
    
                        //Recursively get the record data for children (depth first)
                        var childData = this.getRecordData.call(this, childRecord);
                        if (childRecord.dirty | childRecord.phantom | (childData != null)){
                            data[association.name].push(childData);
                            record.setDirty();
                        }
                    }, me);
                }
            }
            return data;
        }
    });

  6. #26
    Sencha Premium Member
    Join Date
    May 2010
    Location
    Guatemala, Central America
    Posts
    1,322
    Vote Rating
    177
    Answers
    8
    ssamayoa is a splendid one to behold ssamayoa is a splendid one to behold ssamayoa is a splendid one to behold ssamayoa is a splendid one to behold ssamayoa is a splendid one to behold ssamayoa is a splendid one to behold ssamayoa is a splendid one to behold

      0  

    Default


    Here's my code, which works flawlessly.


    This is for 4.0.x or 4.1?

    What about deleted records?

    Original chrisface's code added deleted records with new field called "forDeletion".

    Regards.
    UI: Sencha Architect 3.x / ExtJS 4 & 5
    Server side: JEE / EJB 3.x / CDI / JPA 2.x/ JAX-RS / JasperReports
    Application Server: Glassfish / WildFly
    Databases: Oracle / DB2 / MySQL / Firebird

    If you like my answer please vote!

  7. #27
    Sencha User olivierpons's Avatar
    Join Date
    Dec 2009
    Location
    Aix en Provence,France
    Posts
    116
    Vote Rating
    1
    Answers
    6
    olivierpons is on a distinguished road

      0  

    Default


    The other didn't work for insertion with both Ext 4.0 and Ext 4.1, whereas my solution works for both.
    But I still haven't tested yet deletion. I'll tell you.

  8. #28
    Sencha User olivierpons's Avatar
    Join Date
    Dec 2009
    Location
    Aix en Provence,France
    Posts
    116
    Vote Rating
    1
    Answers
    6
    olivierpons is on a distinguished road

      0  

    Default


    As for deleted records, I just send the whole "current" records, which implies deletion then re-write of records.

    It's acceptable in all the situations where there's a very few recursive nested hasMany relationship (usually no recursive). But you're right, I should mention it.

    In my case it's perfect and works like a charm, and it may help others.

    Maybe someone could change my code just to add deleted record (or something).

  9. #29
    Sencha Premium Member
    Join Date
    Jul 2012
    Posts
    8
    Vote Rating
    1
    bldoron is on a distinguished road

      0  

    Default Has this been fixed?

    Has this been fixed?


    I'm having trouble writing to the server anything with association.
    Also nested items that have only been mapped (not associated):
    like:
    {
    test: {
    auto: {
    users: 40;
    delay: 60;
    }
    }
    }

    When I map it fir the reader as:
    {name: 'autoUsers', mapping: 'auto.users', type: 'number'},
    {name: 'autoDelay', mapping: 'auto.delay', type: 'number'}

    The writer writes the submitted ajax as :
    {
    autoUsers: 40;
    autoDelay: 60;
    }

    But I need it to be returned in the same way that he got it.
    Any help?

  10. #30
    Ext JS Premium Member
    Join Date
    Jan 2010
    Location
    San Diego, CA
    Posts
    254
    Vote Rating
    5
    Answers
    5
    dbrin is on a distinguished road

      0  

    Default


    Use nameProperty: mapping config for the writer - see docs