1. #11
    Sencha Premium Member
    Join Date
    Feb 2011
    Posts
    12
    Vote Rating
    0
    Qanik is on a distinguished road

      0  

    Default


    This is REALLY counter-intuitive. Are there any plans to improve Ext.create(..) to load nested data?

  2. #12
    Sencha User
    Join Date
    Apr 2009
    Posts
    13
    Vote Rating
    1
    matthewdfleming is on a distinguished road

      0  

    Default Here's how we did create...

    Here's how we did create...


    We had this same problem in sencha touch.. the solution was like this..

    This code is in a button click handler for a 'create new assessment' button. The idea is that a create call is supposed to be made to the server and the result of that call should be an 'Assessment.' The json stream coming back has entries for all of the related items (e.g. categories, assessor, etc).

    Code:
            var me = this;
    
    
            // create a new assessment with the client and tool
            var newOne = Ext.create('helium.model.Assessment');
    
    
            // save it so that the 'create' call will happen on the server
            newOne.save({
                success: function (record) {
                    // for some reason the 'create' flow doesn't resolve associations
                    // need to manually push through a reader
                    var reader = Ext.create('Ext.data.reader.Json', {
                        model: 'helium.model.Assessment'
                    });
                    var resultSet = reader.read(record.data); //the record itself won't work, just the .data portion
                    var assessment = resultSet.getRecords()[0];
                    // 'assessment' is now fully read in with associations
                },
                scope: me
            });
    Here's the model object (using a REST proxy).. the writer is a custom writer that will also push all of the related items down on a save/update call..
    Code:
    Ext.define('helium.model.Assessment', {
    
    
        extend: 'Ext.data.Model',
        config: {
            useCache: false,
            fields: [
                'id', 'version', 'startDate', 'type',
                {name: 'allowedActions', persist: false},
                {name: 'status', persist: false},
                {name: 'baseType', persist: false},
                {name: 'major', persist: false},
                {name: 'minor', persist: false}
    
    
            ],
            hasMany: [
                {model: 'helium.model.Category', name: 'categories'}
            ],
            hasOne: [
                {model: 'helium.model.Assessor'},
                {model: 'helium.model.OrgUnit'},
                {model: 'helium.model.Client'}
            ],
            proxy: {
                type: 'rest',
                writer: Ext.create('helium.util.IncludeRelationshipJsonWriter'),
                api: {
                    create: '../data/assessment/create',
                    read: '../data/assessment/read',
                    update: '../data/assessment/update',
                    destroy: '../data/assessment/delete'
                },
                reader: {
                    type: 'json',
                    rootProperty: 'assessment'
                }
            }
        }
    });

  3. #13
    Sencha User
    Join Date
    Jul 2010
    Posts
    50
    Answers
    1
    Vote Rating
    -1
    hdave is an unknown quantity at this point

      0  

    Default


    Yet another person that found this thread. I am using 4.2.2 and am frankly stunned that this doesn't work. I really hate the idea of creating a reader just to do this....

  4. #14
    Sencha User
    Join Date
    Apr 2014
    Posts
    3
    Vote Rating
    0
    adrian_crouch is on a distinguished road

      0  

    Default


    It neither seems to work with extjs 5 ..

  5. #15
    Sencha User
    Join Date
    Oct 2011
    Posts
    11
    Vote Rating
    0
    rockskull is on a distinguished road

      0  

    Default Same problema here, but no solution found

    Same problema here, but no solution found


    Sorry, to revive this thread, but I'm having the same problem.


    I Have a Role screen. This rola has many permissions.

    I want to make a form, that you can fill up the role fields, and add permissions to a grid (related to this role).

    I though in using role.permissions() as the grid's store. But them I saw it's not possible.

    I also wanted to load (in case of editing a role) all role's permissions to a grid, so the user can edit this permissions.

    I'm loading the json once, something like:
    Code:
    {role: {name: "", description: "", permissions: [{name: ""}, {name: ""}]}}
    And I also wanted to send this as a whole to the server, so it will save the role and permissions in a single request.

    I saw the helium example above, but the importante part to me (the helium.util.IncludeRelationshipJsonWriter) ) was not shared (the code).

    How could that be done ?

  6. #16
    Sencha User
    Join Date
    Apr 2009
    Posts
    13
    Vote Rating
    1
    matthewdfleming is on a distinguished road

      0  

    Default IncludeRelationshipJsonWriter.js

    IncludeRelationshipJsonWriter.js


    This is what we used, that totally worked but it's been a while. Good luck..

    Code:
    /**
     * This class extends the normal Json writer so that related objects are sent down to the server on the CRUD operations.
     * The default Sencha writer only sends the data from the top level object being saved. I found two ways to send
     * down the relationship/nested data, one is to define a field using the same name as the relationship (don't do
     * this), and two to write a writer that does it (this class).
     *
     * The problem with the first solution is this.. If you define an object like this:
     *         fields: [
     *          .. other fields
     *          {name: 'anotherField', persist: false},
     *          {name: 'categories'}
     *         ],
     *         hasMany: [
     *          {model: 'helium.model.Category', name: 'categories'}
     *         ],
     *
     * In the definition above there is a field that is the same name as the relationship (hasMany). The problem is
     * that the attribute 'persist' will not be respected for any of the Category objects.. So if you want to exclude
     * attributes from being sent to the server (persisted), the above workaround won't do it.
     *
     * This class does solve that problem and you should not define a 'field' with the same name as an association.
     *
     * This is an amalgam of the ideas @:
     * http://www.sencha.com/forum/showthread.php?141957-Saving-objects-that-are-linked-hasMany-relation-with-a-single-Store
     */
    Ext.define('helium.util.IncludeRelationshipJsonWriter', {
        extend: 'Ext.data.writer.Json',
        /*
         * This function overrides the default implementation of json writer. Any hasMany relationships will be submitted
         * as nested objects. When preparing the data, only children which have been newly created, modified or marked for
         * deletion will be added. To do this, a depth first bottom -> up recursive technique was used.
         */
        getRecordData: function (record) {
            //Setup variables
            var isPhantom = record.phantom === true,
                writeAll = this.getWriteAllFields() || isPhantom,
                me = this, i, association, childStore, data = {};
            if (writeAll) {
                data = me.callParent(arguments);
            } else {
                var changes, name, field, fields = record.fields, nameProperty = me.nameProperty, key;
                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 (!record.phantom) {
                    // always include the id for non phantoms
                    data[record.idProperty] = record.getId();
                }
            }
    
    
            //Iterate over all the hasMany associations
            for (i = 0; i < record.associations.length; i++) {
                association = record.associations.get(i);
                if (association.get('type') != "hasmany") {
                    continue;
                }
                data[association.get('name')] = [];
                childStore = record[association.get('storeName')];
    
    
                if (childStore) {
    
    
                    //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 the child was marked dirty or phantom it must be added. If there was data returned that was neither
                         * dirty or phantom, this means that the depth first recursion has detected that it has a child which is
                         * either dirty or phantom. For this child to be put into the prepared data, it's parents must be in place whether
                         * they were modified or not.
                         */
                        if (childRecord.dirty | childRecord.phantom | (childData != null)) {
                            data[association.get('name')].push(childData);
                            record.setDirty();
                        }
                    }, me);
    
    
                    /*
                     * Iterate over all the removed records and add them to the preparedData. Set a flag on them to show that
                     * they are to be deleted
                     */
                    Ext.each(childStore.removed, function (removedChildRecord) {
                        //Set a flag here to identify removed records
                        removedChildRecord.set('forDeletion', true);
                        var removedChildData = this.getRecordData.call(this, removedChildRecord);
                        data[association.get('name')].push(removedChildData);
                        record.setDirty();
                    }, me);
                }
            }
            //Only return data if it was dirty, new or marked for deletion.
            if (record.dirty | record.phantom | record.get('forDeletion')) {
                return data;
            }
        }
    });

  7. #17
    Sencha User
    Join Date
    Apr 2014
    Posts
    3
    Vote Rating
    0
    adrian_crouch is on a distinguished road

      0  

    Default


    As far as i remember we solved that the way skirtle proposed. We've rolled out that functionality into a model utility class and are only working against a ModelUtils#create function when populating an associative model. That works as expected and is the most time-consuming approach w/o implementing too much extra code that might not work anymore in a subsequent release.

    Code:
    Ext.define('ExtBbiAC.ModelUtils', {
    
        statics: {
    
            /**
             * Creates a new pojo with the help of a JSON reader. This allows to create a pojo with associations
             * which is not implemented by the Ext.create() nor Ext.data.Model.create() operation as they do not
             * use a reader when populating a model.
             * @param model Obligatory.
             * @param config Optional.
             */
            create: function(model, config) {
                var reader = Ext.create('Ext.data.reader.Json', {
                    model: model
                });
                var resultSet = reader.read(config);
                return resultSet.records[0];
            }
        }
    }

  8. #18
    Sencha User
    Join Date
    Apr 2009
    Posts
    13
    Vote Rating
    1
    matthewdfleming is on a distinguished road

      0  

    Default One last thought...

    One last thought...


    I've moved away from doing large object graph saves. If I want to save the children pointed to a parent, I'll just send the children. If I want to save a parent object, just save the parent. The trouble starts when you have the idea in your head that you must send the parent object to save the relationships to the children. Or even worse the relationship to the children plus any edits to a child.

    My point is that after getting everything to work, I realized this was really an anti-pattern rather than something that was desirable. I hope the code helps you, but I'd probably move a different direction if you can.