2013-08-16 13 views
10

Sono curioso di sapere perché il record contenuto nel set di risultati di una risposta Model.save() non restituisce correttamente i dati associati aggiornati, nonostante i dati aggiornati siano contenuti nel server risposta ...ExtJS 4.1 - Restituzione dei dati associati nella risposta Model.Save()

Esempio modello & Conservare Definizione: risposta del server

Ext.define("App.model.test.Parent",{ 
    extend: 'Ext.data.Model', 
    requires: ['App.model.test.Child'], 
    fields: [ 
      {name: 'id', type: 'int' }, 
      {name: 'name', type: 'string'}, 
      {name: 'kids', type: 'auto', defaultValue: []} 
    ], 
    idProperty: 'id', 

    hasMany: [{ 
      foreignKey: 'parent_id', 
      model: 'App.model.test.Child', 
      associationKey: 'kids', 
      name: 'getKids' 
    }], 

    proxy: { 
     type: 'ajax', 
     api : { 
      create: '/service/test/create/format/json', 
      read : '/service/test/read/format/json', 
      update : '/service/test/update/format/json' 
     }, 

     reader: { 
      idProperty  : 'id', 
      type   : 'json', 
      root   : 'data',   
      successProperty : 'success',  
      messageProperty : 'message' 
     }, 

     writer: { 
      type   : 'json', 
      writeAllFields : true 
     } 
    } 
}); 

Ext.define("App.model.test.Child",{ 
    extend: 'Ext.data.Model', 
    fields: [ 
     {name: 'id', type: 'int' }, 
     {name: 'name', type: 'string'}, 
     {name: 'parent_id', type: 'int'} 
    ] 
}); 

Ext.define("App.store.test.Simpson",{ 
    storeId: 'TheSimpsons', 
    extend: 'Ext.data.Store', 
    model : 'App.model.test.Parent', 
    autoLoad: true, 
    autoSync: false 
}); 

la domanda di READ richiesta del proxy con un unico modello, e la sua dati associati. Tutto questo funziona hunky dory!

Risposta del server di richiesta di lettura

{ 
"data":{ 
    "id":1, 
    "name":"Homer Simpson", 
    "children":{ 
     "1":{ 
      "id":1, 
      "name":"Bart Simpson" 
     }, 
     "2":{ 
      "id":2, 
      "name":"Lisa Simpson" 
     }, 
     "3":{ 
      "id":3, 
      "name":"Maggie Simpson" 
     } 
    } 
}, 
"success":true, 
"message":null 
} 

Finora, tutto funziona secondo i piani ...

store = Ext.create("App.store.test.Simpson"); 
homer = store.getById(1); 
kids = homer.getKids().getRange(); 
console.log("The Simpson Kids", kids); // [>constructor, >constructor, >constructor] 

IL comportamento indesiderato INIZIA CON salvare e aggiornare RICHIESTE

Ecco la mia risposta di prova per la UPDATE Richiesta ...

/** Server UPDATE Response */ 
{ 
"data":{ 
    "id":1, 
    "name":"SAVED Homer Simpson", 
    "kids":[{ 
     "id":1, 
     "name":"SAVED Bart Simpson", 
     "parent_id":1 
    },{ 
     "id":2, 
     "name":"SAVED Lisa Simpson", 
     "parent_id":1 
    },{ 
     "id":3, 
     "name":"SAVED Maggie Simpson", 
     "parent_id":1 
    }] 
}, 
"success":true, 
"message":null 
} 


/** Will call proxy UPDATE, response is above */ 
homer.save({ 
    success: function(rec, op){ 
     var savedRec = op.getRecords().pop(), 
      kidNames = ''; 
     console.log(savedRec.get('name')); // SAVED Homer Simpson = CORRECT! 
     Ext.each(savedRec.getKids().getRange(), function(kid){ 
      kidNames += kid.get('name') + ", "; 
     }); 
     console.log(kids); 
     //Outputs: Bart Simpson, Lisa Simpson, Maggie Simpson = WRONG!! 
    } 
}) 

mi accorgo che se io esaminare il record restituito dal server, l'Associazione generato Store (vale a dire, getKidsStore) i record contenuti sono l'originale record, cioè, non hanno "SALVATO" nel loro nome. La proprietà kids del record restituito, tuttavia, contiene effettivamente i dati corretti.

Se ho compreso correttamente il problema, è che lo Ext.data.reader.Reader non aggiorna correttamente l'archivio associato con i dati associati contenuti nella risposta .save(). In tal caso, a mio parere, questo è molto intuitivo in quanto mi aspetterei lo stesso comportamento del lettore che gestisce la richiesta store.load() e popola gli archivi di associazione generati per iniziare.

Qualcuno può indicarmi la giusta direzione per ottenere il comportamento che sto cercando?

Disclaimer: La stessa domanda è stata posta qui: ExtJs 4 - Load nested data on record save ma senza risposta. Sento la mia domanda per essere un po 'più approfondita ..

EDIT: Ho postato questa domanda più sul forum di Sencha: http://www.sencha.com/forum/showthread.php?270336-Associated-Data-in-Model.save()-Response

EDIT (8/23/13): I Re- ha scritto questo post con un esempio completo, così come i risultati supplementari ...

risposta

6

ho trovato il problema, o meglio, la confusione sta nel metodo della Ext.data.OperationgetRecords(). Questo metodo restituisce "verranno restituiti i record inizialmente configurati dell'operazione, sebbene il proxy possa modificare i dati di tali record in qualche momento dopo l'inizializzazione dell'operazione." come da documentazione.

Questo è un IMO piuttosto confuso, in quanto il record restituito viene effettivamente aggiornato, tuttavia l'archivio delle associazioni generato e quindi i dati associati non lo sono! Questo è ciò che ha portato alla mia confusione, sembrava che il record contenesse i dati aggiornati dal server delle applicazioni, ma non era così.

Al fine di aiutare la mia mente semplice per ottenere i COMPLETAMENTE dati aggiornati dalla risposta, ho aggiunto un metodo alla classe Ext.data.Operation ... Ho appena scritto questo metodo e non l'ho testato più di garantire la funzionalità che stavo cercando, quindi usare a proprio rischio!

prega di tenere presente che io non chiamo store.sync(), piuttosto ho un'istanza di un modello di e chiamare il metodo model.save(), quindi il mio resultSet tipicamente sempre e solo contiene un singolo 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!"; 
     } 

    } 
}); 

Ora sono in grado di ottenere la funzionalità che cercavo ...

// 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 ho anche aggiunto un metodo per Ext.data.Model che ho chiamato updateTo che gestisce l'aggiornamento di un record alla fornito record, che anche gestisce le associazioni. Lo uso insieme al metodo getSavedRecord precedente. Si noti che questo non gestisce alcuna associazione belongsTo poiché non li utilizzo nella mia applicazione, ma tale funzionalità sarebbe facile da aggiungere.

/** 
* 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; 
} 

Quindi, fondamentalmente faccio qualcosa di simile (questo sarebbe teoricamente nel controller Order)

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; 
      } 
     } 
    }); 
} 

Spero che questo aiuta qualcuno che è stato confuso come lo ero io!

3

Completamente d'accordo con te. Comportamento davvero strano Dovrebbe aggiornare l'archivio delle associazioni sul record. Questo è il modo ho avuto questo problema (in pratica solo correre la risposta attraverso il lettore!):

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

Buona soluzione! Per qualche motivo, ho riscontrato che lo Store non si aggiorna sempre quando si richiama manualmente l'operazione di lettura del Proxy dal Modello ... Quindi ho aggiunto un metodo 'updateTo' sulla classe Model, che gestisce l'aggiornamento di un'istanza al fornito record (recuperato dalla risposta del server), inclusi i negozi di associazioni. Stessa idea, solo approccio diverso :) –

+0

Aggiunto il metodo 'updateTo' alla mia risposta per essere più chiaro/utile. –

-1

Se il campo ID ha un valore, allora ExtJS sarà sempre chiamare aggiornamento. Se non si scrive alcun valore nel campo ID o si imposta su null, è necessario chiamare create. Immagino tu stia cercando di chiamare il salvataggio con un record esistente, quindi chiamerà sempre l'aggiornamento. Questo è un comportamento desiderato.

+1

La domanda non ha nulla a che fare con le richieste di creazione/aggiornamento ... ha solo a che fare con il ritorno dei dati associati e ExtJS aggiorna il record del negozio di conseguenza, che NON funziona affatto ... come dimostrato. Il record deve essere completamente ricaricato dal server (forzando un round trip inutile) o aggiornato manualmente utilizzando i dati forniti nella risposta del server, il cui processo è descritto a fondo nella mia risposta. –

Problemi correlati