Pregunta

I'm trying to handle JSON with nested structure with ExtJS4. Please do not answer like here because it's wrong answer. I use the expandData: true with model mappings and it works for me really fine.

The problem I expect is with one field that is array of objects. So, here is my code sample:

Ext.define('EdiWebUI.model.Document', {
  extend: 'Ext.data.Model',
  fields: [
    {name: 'document_header_documentReceiveDateTime', mapping: 'document.header.documentReceiveDateTime', type: 'string'},
    {name: 'document_header_documentProcessDateTime', mapping: 'document.header.documentProcessDateTime', type: 'string'},
    {name: 'document_header_documentID', mapping: 'document.header.documentID', type: 'string'},
    ...
    {name: 'lines', type: 'auto'},
    ...
    {name: 'attachments_documentFile_fileName', mapping: 'attachments.documentFile.fileName', type: 'string'},
    {name: 'attachments_documentFile_content', mapping: 'attachments.documentFile.content', type: 'string'}
  ],
  hasMany: [
    {model: 'DocumentLines', name: 'lines', associationKey: 'lines'}
  ],
  proxy: {
    type: 'rest',
    url: '/document',
    reader: {
      type: 'json',
      root: 'data'
    },
    writer: {
      expandData: true,
      writeAllFields: true,
      nameProperty: 'mapping'
    }
  }
});

Ext.define('DocumentLines',{
  extend: 'Ext.data.Model',
  fields: [
    {'name': 'line_lineItem_lineNumber', mapping: 'line.lineItem.lineNumber', type: 'string'},
    {'name': 'line_lineItem_orderedQuantity', mapping: 'line.lineItem.orderedQuantity', type: 'string'},
    {'name': 'line_lineItem_orderedUnitPackSize', mapping: 'line.lineItem.orderedUnitPackSize', type: 'string'},
    ...
});

So, it working well when reading JSON like this:

{
  "data": {
    "document": {
      "header": {
        "documentReceiveDateTime": "2014-03-25T08:34:24",
        "documentProcessDateTime": "2014-03-25T08:44:51",
        "documentID": "83701540",
        ...,
        "lines": [
          {
            "line": {
              "lineItem": {
                "lineNumber": "1",
                "orderedQuantity": "5.000",
                "orderedUnitPackSize": "1.000"
              }
            }
          },
          {
            "line": {
              "lineItem": {
                "lineNumber": "2",
                "orderedQuantity": "4.000",
                "orderedUnitPackSize": "1.000"
              }
            }
          }
        ]
        ...

but I can't make writer to parse lines. When I'm truing to save my document I already have output like this:

{ lines: 
   [ { line_lineItem_lineNumber: 1,
       line_lineItem_ean: '4352345234523',
       line_lineItem_orderedQuantity: '45'} ],

(other parts of document are expanded well)

So, here is a question: Is there a way to make it works as I need? ...or I should make a trick on a server side (as I actually do now)...

Thanks in advance.

¿Fue útil?

Solución

You have two choices here:

  • The proper way, which is to use the stores capabilities: define your dataWriter and code your own function in order to get the json you want.
  • Don't use the store to update your records, create the json you want and use an Ajax request to update the records you need to update.

Both ways uses Ajax anyway, the first one should be preferred.

I would define my writer in the same file as the store, something like:

Ext.define('MyApp.custom.Writer',{
    /*
     * Formats the data for each record before sending it to the server. 
     * This method should be overridden to format the data in a way that differs from the default.
     */
    getRecordData: function(record) {
        var data = {};
        /*
         * Parse your record and give it whatever structure you need here..
         */
        data.lines = [];
        return data;
    }
});

Although you seem to have one extra level of indirection in your Json, the "lineItem" is not necessarly needed as you already have a one-to-one relationship between line <-> lineItem and lineItem <-> and the object defined by lineItem. But this is a different question.

Otros consejos

I've used the answer above, but wanted to share the code to make it a little bit easier for people who try the same thing.

Dr. Leevsey's Code from above worked for me but had the disadvantag that it puts everything inside an array. For my project it worked better if it returned an object (with the child objects) and didn't return an array if the base object is not an array.

Here is the code:

Ext.define('MyApp.util.customWriter',
{
    extend: 'Ext.data.writer.Json',
    getRecordData: function (record, operation) {
        var data = record;
        var me = this;
        var toObject = function (name, value) {
            var o = {};
            o[name] = value;
            return o;
        };
        var itemsToObject = function (item) {
            for (prop in item) {
                if (Array.isArray(item[prop])) {
                    me.getRecordData(item[prop]);
                }
                else {
                    if (item.hasOwnProperty(prop)) {
                        var nameParts = prop.split('.');
                        var j = nameParts.length - 1;
                        if (j > 0) {
                            var tempObj = item[prop];
                            for (; j > 0; j--) {
                                tempObj = me.toObject(nameParts[j], tempObj);
                            }
                            item[nameParts[0]] = item[nameParts[0]] || {};
                            Ext.Object.merge(item[nameParts[0]], tempObj);
                            delete item[prop];
                        }
                    }
                }
            }
        };

        if (!Array.isArray(data)) {
            data = data.getData();
            itemsToObject(data);
        }
        else {
            var dataLength = data.length;
            for (var i = 0; i < dataLength; i++) {
                itemsToObject(data[i]);
            }
        }

        return data;
    }
});
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top