ExtJS 4.1 - Returning Associated Data in Model.Save() Response

こ雲淡風輕ζ 提交于 2019-11-30 17:44:31

I've found the issue, or rather, confusion lies in the getRecords() method of the Ext.data.Operation. This method returns "the operation's initially configured records will be returned, although the proxy may modify these records' data at some point after the operation is initialized." as per the documentation.

This is rather confusing IMO, as the returned record is indeed updated, however the generated association store, and therefore associated data, is not! This is what lead to my confusion, it appeared as though the record contained the updated data from the application server, but this was not the case.

In order to aid my simple mind in obtaining the FULLY updated data from the response, I have added a method to the Ext.data.Operation class... I just wrote this method and haven't tested it more than ensuring the functionality I was looking for, so use at your own risk!

Please keep in mind that I do not call store.sync(), rather I instantiate a model and call the model.save() method, so my resultSet typically only ever contains a single record...

Ext.override(Ext.data.Operation,{
    getSavedRecord: function(){
        var me = this, // operation
            resultSet = me.getResultSet();

        if(resultSet.records){
            return resultSet.records[0];
        }else{
            throw "[Ext.data.Operation] EXCEPTION: resultSet contains no records!";
        }

    }
});

Now I am able to achieve the functionality I was after...

// Get the unsaved data
store = Ext.create('App.store.test.Simpson');
homer = store.getById(1);
unsavedChildren = '';

Ext.each(homer.getKids().getRange(), function(kid){
    unsavedChildren += kid.get('name') + ",";
});

console.log(unsavedChildren); // Bart Simpson, Lisa Simpson, Maggie Simpson

// Invokes the UPDATE Method on the proxy
// See original post for server response
home.save({
    success: function(rec, op){
        var savedRecord = op.getSavedRecord(), // the magic! /sarcasm
            savedKids   = '';

        Ext.each(savedRecord.getKids().getRange(), function(kid){
            savedKids += kid.get('name') + ',';
        });

        console.log("Saved Children", savedKids);

        /** Output is now Correct!!
            SAVED Bart Simpson, SAVED Lisa Simpson, SAVED Maggie Simpson
          */
    }
});

Edit 12/10/13 I also added a method to Ext.data.Model which I called updateTo which handles updating a record to the provided record, which also handles associations. I use this in conjunction with the above getSavedRecord method. Please note this does not handle any belongsTo associations as I don't use them in my application, but that functionality would be easy to add.

/**
 * Provides a means to update to the provided model, including any associated data
 * @param {Ext.data.Model} model The model instance to update to. Must have the same modelName as the current model
 * @return {Ext.data.Model} The updated model
 */
updateTo: function(model){
    var me = this,
    that = model,
    associations = me.associations.getRange();

    if(me.modelName !== that.modelName)
    throw TypeError("updateTo requires a model of the same type as the current instance ("+ me.modelName +"). " + that.modelName + " provided.");

    // First just update the model fields and values
    me.set(that.getData());

    // Now update associations
    Ext.each(associations, function(assoc){
    switch(assoc.type){
        /**
         * hasOne associations exist on the current model (me) as an instance of the associated model.
         * This instance, and therefore the association, can be updated by retrieving the instance and
         * invoking the "set" method, feeding it the updated data from the provided model.
         */
        case "hasOne":
            var instanceName  = assoc.instanceName,
                currentInstance = me[instanceName],
                updatedInstance = that[instanceName];

             // Update the current model's hasOne instance with data from the provided model
             currentInstance.set(updatedInstance.getData());

            break;

        /** 
         * hasMany associations operate from a store, so we need to retrieve the updated association
         * data from the provided model (that) and feed it into the current model's (me) assocStore
         */
        case "hasMany":
            var assocStore = me[assoc.storeName],
                getter     = assoc.name,
                newData    = that[getter]().getRange();

            // Update the current model's hasMany association store with data from the provided model's hasMany store
            assocStore.loadData(newData);
            break;

        // If for some reason a bogus association type comes through, throw a type error
        // At this time I have no belongsTo associations in my application, so this TypeError
        // may one day appear if I decide to implement them.
        default:
            throw TypeError("updateTo does not know how to handle association type: " + assoc.type);
            break;
    }
    });

    // Commit these changes
    me.commit();

    return me;
}

So basically I do something like this (this would theoretically be in the Order controller)

doSaveOrder: function(order){
    var me = this,                       // order controller 
        orderStore = me.getOrderStore(); // magic method

    // Save request
    order.save({
        scope: me,
        success: function(responseRecord, operation){ 
            // note: responseRecord does not have updated associations, as per post
            var serverRecord = operation.getSavedRecord(),
                storeRecord  = orderStore.getById(order.getId());

            switch(operation.action){
                case 'create':
                    // Add the new record to the client store
                    orderStore.add(serverRecord);
                break;

                case 'update':
                    // Update existing record, AND associations, included in server response
                    storeRecord.updateTo(serverRecord);
                break;
            }
        }
    });
}

I hope this helps someone who was confused as I was!

Completely agree with you. Really odd behavior. It should update the association store on the record. This is how i got around this issue (basically just run the response through the reader!):

 success: function(record, operation) {
     var newRecord= me.getMyModel().getProxy().reader.read(operation.response).records[0];
 }

If your ID field has a value then ExtJS will always call update. If you do not write any value to your id field or set it to null, it should call create. I guess you are trying to call save with an existing record so it will always call update. This is a desired behavior.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!