2013-08-16 32 views
10

Jestem ciekawy, dlaczego rekord zawarty w zestawie wyników odpowiedzi Model.save() nie zwraca prawidłowo zaktualizowanych powiązanych danych, mimo że zaktualizowane dane są zawarte na serwerze odpowiedź ...ExtJS 4.1 - Zwracanie skojarzonych danych w Model.Save() Odpowiedź

modelowym przykładem & Store Definicja:

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

aplikacja odpowiedź serwera proxy READ życzenie z jednego modelu i związanej z nią danych. To wszystko działa hunky dory!

Odpowiedź serwera na żądanie odczytu

{ 
"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 
} 

dotąd wszystko działa zgodnie z planem ...

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

niepożądanego ZACHOWANIE ROZPOCZYNA SIĘ zapisać i UPDATE WNIOSKI

Oto moja odpowiedź testem dla UPDATE Zamówienie ...

/** 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!! 
    } 
}) 

Zauważyłem, że jeśli będę sprawdzać rekord zwrócony przez serwer, generowanym Stowarzyszenia Store (tj getKidsStore) Ograniczone zapisy są oryginalne rekordy, tj. nie mają "ZAPISANE" w ich nazwie. Właściwość kids zwróconego rekordu rzeczywiście zawiera poprawne dane.

Jeśli dobrze rozumiem problem, jest to, że Ext.data.reader.Reader nie aktualizuje poprawnie powiązany sklep z odpowiednimi danymi zawartymi w odpowiedzi .save(). Jeśli tak, to moim zdaniem jest to bardzo nieintuicyjne, ponieważ oczekiwałbym takiego samego zachowania, jak czytnik, który obsługuje żądanie store.load() i na początek zapełnia wygenerowane magazyny powiązań.

Czy ktoś może wskazać mi właściwy kierunek w osiąganiu pożądanego zachowania?

Nota prawna: To samo pytanie zostało zadane tutaj: ExtJs 4 - Load nested data on record save, ale bez odpowiedzi. Czuję moje pytanie będzie nieco bardziej dokładne ..

EDIT: Pisałem to pytanie nad na forach Sencha: http://www.sencha.com/forum/showthread.php?270336-Associated-Data-in-Model.save()-Response

EDIT (23.08.13): I re- napisałem ten post z kompletnym przykład, jak również dodatkowe wnioski ...

Odpowiedz

6

znalazłem problem, a raczej zamieszanie leży w sposobie Ext.data.OperationgetRecords(). Ta metoda zwraca "pierwotnie skonfigurowane rekordy operacji zostaną zwrócone, chociaż proxy może modyfikować dane tych rekordów w pewnym momencie po zainicjowaniu operacji." zgodnie z dokumentacją.

To jest dość zagmatwana IMO, ponieważ zwrócony rekord jest rzeczywiście zaktualizowany, jednak wygenerowany magazyn powiązań, a więc powiązane dane, nie jest! To właśnie doprowadziło do mojego zamieszania, wydawało się, że rekord zawiera zaktualizowane dane z serwera aplikacji, ale tak nie było.

W celu ułatwienia mój prosty umysł w uzyskaniu pełni aktualizowane dane z odpowiedzi dodałem metodę klasy Ext.data.Operation ... Ja tylko napisałem ten sposób i nie testowano go bardziej niż upewniając się, że funkcjonalność, której szukałem, więc korzystaj z niej na własne ryzyko!

Proszę pamiętać, że ja nie nazywam store.sync(), a I instancji modelu i wywołać metodę model.save(), więc moja resultSet zazwyczaj zawiera zawsze tylko jeden rekord ...

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!"; 
     } 

    } 
}); 

teraz jestem w stanie osiągnąć funkcjonalność byłem po ...

// 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 10.12.13 Dodałem też metodę Ext.data.Model który nazwałem updateTo który obsługuje uaktualniania do rekordu pod warunkiem, że rekord, który również obsługuje stowarzyszenia. Używam tego w połączeniu z powyższą metodą getSavedRecord. Pamiętaj, że to nie obsługuje żadnych powiązań belongsTo, ponieważ nie używam ich w mojej aplikacji, ale ta funkcja byłaby łatwa do dodania.

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

Więc w zasadzie ja coś takiego zrobić (to teoretycznie być w sterowniku zamówienia)

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

Mam nadzieję, że to pomoże kogoś, kto był zdezorientowany jak ja!

3

Całkowicie się z tobą zgadzam. Naprawdę dziwne zachowanie. Powinno zaktualizować magazyn powiązań na płycie. W ten sposób mam ten problem (w zasadzie wystarczy uruchomić reakcję przez czytelnika!):

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

Dobre rozwiązanie! Z jakiegoś powodu odkryłem, że Store nie zawsze aktualizuje się przy ręcznym wywoływaniu operacji odczytu Proxy z Modelu ... Dlatego dodałem metodę "updateTo" w klasie Model, która obsługuje aktualizowanie instancji do dostarczonej rekord (pobrany z odpowiedzi serwera), w tym składnice stowarzyszeń. Ten sam pomysł, tylko inne podejście :) –

+0

Dodałem metodę "updateTo" do mojej odpowiedzi, aby było bardziej zrozumiałe/pomocne. –

-1

Jeśli pole ID ma wartość wtedy ExtJS zawsze zadzwonić aktualizację. Jeśli nie napiszesz żadnej wartości do pola id lub ustawisz na wartość null, powinno się wywołać create. Domyślam się, że próbujesz wywołać zapis z istniejącym rekordem, więc zawsze będzie on wywoływał aktualizację. To pożądane zachowanie.

+1

Pytanie nie ma nic wspólnego z żądaniami tworzenia/aktualizacji ... dotyczy wyłącznie zwracania powiązanych danych, a ExtJS aktualizuje odpowiednio rekord sklepu, który w ogóle NIE działa ... jak pokazano. Rekord musi zostać całkowicie przeładowany z serwera (wymuszenie bezużytecznego podróżowania w obie strony) lub ręcznie zaktualizowany przy użyciu danych podanych w odpowiedzi serwera, których proces jest dokładnie opisany w mojej odpowiedzi. –