1. #1
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    -1
    mabello is an unknown quantity at this point

      0  

    Default About DataReader class hierarchy

    About DataReader class hierarchy


    I was looking to the surce code of DataReader and all the sublcasses of that, and I found something that I'd like to discuss.

    Without looking at the source code at the beginning, I though that the dataReaders must be interchangeable each other without problem based on what data you need to read, so that the DataProxy can use whatever DataReader (Array Json, ...)

    JsonReader defines 2 public method read and readRecords, but the comment say that read is only used by a DataProxy (meaning that DataProxy can not use ArrayReader correctly in my opinion)
    PHP Code:
        /**
         * This method is only used by a DataProxy which has retrieved data from a remote server.
         * @param {Object} response The XHR object which contains the JSON data in its responseText.
         * @return {Object} data A data block which is used by an Ext.data.Store object as
         * a cache of Ext.data.Records.
         */
        
    read : function(response){ 
    Analyzing ArrayReader, you can see that it does not defined read, so it inherits read function from JsonReader, but of course in my opinion it is not a clear design, because I think you can not call read in a instance of ArrayReader otherwise you will have an error, so that you have ArrayReader and JsonDataReader with a "different interface contract"; furthemore, ArrayReader , a class that extend JsonReader has "less" functionality than the base class...so why inherit ArrayReader from JsonReader, if they not share anything and there is no one call to the super class?

    Besides, in case you want to use an ArrayReader within a grid with pagination (I mean the store linked to the grid is using an instance of ArrayReader) your paging toolbar will have problem to display the real totalRecord of your dataset, because the property totalRecords is calculated based on the length of the element cotained in the underlying array.
    PHP Code:
     readRecords : function(o){
            var 
    sid this.meta this.meta.id null;
            var 
    recordType this.recordTypefields recordType.prototype.fields;
            var 
    records = [];
            var 
    root o;
            for(var 
    0root.lengthi++){
                var 
    root[i];
                var 
    values = {};
                var 
    id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" n[sid] : null);
                for(var 
    0jlen fields.lengthjlenj++){
                    var 
    fields.items[j];
                    var 
    f.mapping !== undefined && f.mapping !== null f.mapping j;
                    var 
    n[k] !== undefined n[k] : f.defaultValue;
                    
    f.convert(vn);
                    
    values[f.name] = v;
                }
                var 
    record = new recordType(valuesid);
                
    record.json n;
                
    records[records.length] = record;
            }
            return {
                
    records records,
                
    totalRecords records.length//************************
            
    };
        } 
    So in my application I had to override the read function of array to have the same functionality of a JsonReader (I had to do so because I have impemented a specialized DataProxy used with AjaxPro, but it is not the point)

    PHP Code:
    Ext.override(Ext.data.ArrayReader, {
        
    read : function(reponse) {
            var 
    reponse;
            if(
    typeof this.meta.beforeReadRecords == 'function' && this.meta.beforeReadRecords != null)
                
    this.meta.beforeReadRecords(reponse);
            var 
    result this.readRecords(o);
            if(
    typeof this.meta.afterReadRecords == 'function' && this.meta.afterReadRecords != null)
                 
    this.meta.afterReadRecords(resultreponse);
            return 
    result;
        }
    }); 
    In beforeReadRecords, you can pre-elaborate your response.
    In afterReadRecords, you can modify the result to achieve your goals (for example to set correctly to the result the property totalRecords based on the response received from the server.

    That's how i use the array reader in my code:
    PHP Code:
    var dataReaderMappingCfg = [{namethis.dataFieldName}];
            
            var 
    meta = {
            
                
    dataSetPropertyNameArray: ['value''dataSet'],
               
                
    totalCountsPropertyNameArray: ['value''totalCounts'],
                
                
    beforeReadRecords : function(response){
                    return 
    TI.Util.PropertyFinder(responsethis.dataSetPropertyNameArray)
                },
                
    afterReadRecords : function(resultresponse){
                    
    result.totalRecords TI.Util.PropertyFinder(responsethis.totalCountsPropertyNameArray);
                }
            }
            
    this.dataReader = new Ext.data.ArrayReader(metadataReaderMappingCfg); 
    What do you think?

    Regards

  2. #2
    Sencha - Ext JS Dev Team Animal's Avatar
    Join Date
    Mar 2007
    Location
    Notts/Redwood City
    Posts
    30,506
    Vote Rating
    54
    Animal has a spectacular aura about Animal has a spectacular aura about Animal has a spectacular aura about

      0  

    Default


    Perhaps JsonReader should extend Observable as well as DataReader, and offer events to allow preprocessing and postprocessing of recieved data.

  3. #3
    Sencha User jack.slocum's Avatar
    Join Date
    Mar 2007
    Location
    Tampa, FL
    Posts
    6,955
    Vote Rating
    17
    jack.slocum will become famous soon enough jack.slocum will become famous soon enough

      0  

    Default


    Why not subclass? That's what OO is all about and that is what we do with our classes internally. You should do the same.

    As for the interface contract, it is identical for both ArrayReader and JsonReader. There is nothing in the interface that guarantees they will have the same functionality internally in those functions. In fact, it is highly unlikely they will since then you wouldn't need 2 classes.
    Jack Slocum
    Ext JS Founder
    Original author of Ext JS 1, 2 & 3.
    Twitter: @jackslocum
    jack@extjs.com

  4. #4
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    -1
    mabello is an unknown quantity at this point

      0  

    Default


    Why not subclass? That's what OO is all about and that is what we do with our classes internally. You should do the same
    Yes I agree with you Jack, but for me ArrayReader subclassing JsonReader does not make a lot of sense if the read function that ArrayReader inherit from JsonReader does not work for it (but if it works then ok)

    There is nothing in the interface that guarantees they will have the same functionality internally in those functions
    But a client A has to be sure that if it uses an instance of ArrayReader or JsonReader will have the same result, if one is a subclass of the other.

    As i said before, as at the moment ArrayReader inherit from JsonReader,it has the same function read of JsonReader, but it is what you want?
    I will be more clear.

    Let say that you want to use HttpProxy with a ArrayReader (why not? HttpProxy cares only that a reader has to have a function like this:
    HTML Code:
    read(response)
    and I will expect that if I have set up correctly my DataReader (ArrayReader, JsonReader, MyDataReaderImplementation...), I won't have any exception throwed by read.read of an instance of HttpProxy(as long as the response received from my server and passed to the reader (reader.read(response) is correct).
    ).

    Regards

    EDIT:

    Jack maybe I miss understud. So the read function that ArrayReader inherits works fine also for the ArrayReader?
    if so, ArrayReader subclassing JSonReader make sense as you said Jack...

    Anyway, the other point is that if you need Pagination with an ArrayReader, you can not have it, I mean you can have it but
    the totalrecords will be wrong; an interceptors (or sending pre-post processing events) like I suggest could help I think...
    Last edited by mabello; 3 Apr 2008 at 11:30 AM. Reason: Corrections

  5. #5
    Sencha User jack.slocum's Avatar
    Join Date
    Mar 2007
    Location
    Tampa, FL
    Posts
    6,955
    Vote Rating
    17
    jack.slocum will become famous soon enough jack.slocum will become famous soon enough

      0  

    Default


    It seems like what you are looking for is actually a JsonReader. e.g. you want to include paging data in your response, which means it won't be just a js array, it will be actually be a JSON object. The data within that JSON object can be plain array data and you should still be able to be read it with a JsonReader, but you'd have to set up mappings... e.g.

    Code:
    ...
    fields: [
        {name: 'myField', mapping: 0} // map to array index 0
        ...
    ]
    In theory this should work fine, but I have never tried it.
    Jack Slocum
    Ext JS Founder
    Original author of Ext JS 1, 2 & 3.
    Twitter: @jackslocum
    jack@extjs.com

  6. #6
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    -1
    mabello is an unknown quantity at this point

      0  

    Default


    Animal, Jack thanks a lot for your kinds reply!
    Jack,
    you are right, I can use a JsonReader as you said I think

    Now I can see better also your point of view: ArrayReader extend JsonReader because for you ArrayReader is actually a JsonReader, I mean that you would like to use the read function also for an ArrayReader, not sure if it works though (and you will have also the problem with the property totalRecords anyway)

    My main view on DataReader is that it is like a "parser" that create, as a result of the parsing of the response, the "AST", that in this case is the object that readRecords of DataReader return.

    Usually, JsonReader and ArrayReader etc will work just fine and will cover in general all the possible scenarios, but let say that you already have some ajax calls that returns a data in a format that it you need to transform/adapte for a JsonReader or an ArrayReader to make them works fine with the response received by HttpProxy for example.

    If you have a data like the following, returning from an ajax call:
    PHP Code:
    ["pippo""pluto""paperino"
    You can not use this type of data neither with JsonReader and ArrayReader without pre-elaborate them (I had anwer someone in the help that was asking actually for something like that); so it means that if you want to use these kind ofarray for populate a combox, you can not, and you do not want to change your server-side code.

    I could find another example that suite also for the JsonReader, in which you need to adapte the response before call readRecords in the JsonReader.

    Of course, what I'd like to do is write the less code that I can (the less the better to maintain), so I would prefer not to create a new class inheriting from JsonReader or ArrayReader only to adapte my returning data, but I would like to pass a function (or make DataReader fire events, better) to the JsonReader or ArrayReader to pre elaborate and adapte my input data in the read function of my DataReader, and just in case, I would give the possibilty to post-elaborate also my result from the readRecords (I actually using these to make ArrayReader behave like a JsonReader in case I need to use pagination).

    Maybe after this discussion, a DataAdapter class could be a solution

    Really thanks, I really enjoy speaking with you guys!! We could go out for a beer sometimes
    Last edited by mabello; 3 Apr 2008 at 1:38 PM. Reason: English

  7. #7
    Sencha User jack.slocum's Avatar
    Join Date
    Mar 2007
    Location
    Tampa, FL
    Posts
    6,955
    Vote Rating
    17
    jack.slocum will become famous soon enough jack.slocum will become famous soon enough

      0  

    Default


    It's pretty easy process to create your own reader to do it:

    Code:
    foo.FlatReader = Ext.extend(Ext.data.ArrayReader, {
         readRecords : function(data){
              var r = [];
              for(var i = 0, len = data.length; i < len; i++){
                  r[i] = [data[i]];
              }
              foo.FlatReader.superclass.readRecords.call(this, data);
         }
    });
    Now you have a new, reusable Reader class that can expand the data you want to read into something Ext can read.
    Jack Slocum
    Ext JS Founder
    Original author of Ext JS 1, 2 & 3.
    Twitter: @jackslocum
    jack@extjs.com

  8. #8
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    -1
    mabello is an unknown quantity at this point

      0  

    Default


    Thanks Jack,
    yes I'm using the same function

    But than what if I have something like:
    PHP Code:
    {["pippo"], ["pluto"]} 
    My point is, you could have a lot of different form of data you would like to transform without actually change the readRecords function for example of JsonReader, that is quite complicated.
    So give the chance to pre and post elaborate the data could make the life of the programmer easer in my opinion.

    You can find the same problem for the TreeLoader, in which you have to receive from the server the exact data ready-to use to create the tree.

    Also in this case, you would like to have a way to pre-process your data so that you can create the right structure to create your tree also in case the same ajax call is used to populate a combobox!

    Also because of that, I have created a TreeLoader extension, that maybe is not really good coding-speaking, but it is only a matter of refactoring and time...

    http://extjs.com/forum/showthread.php?t=26510

    Thanks again
    Last edited by mabello; 3 Apr 2008 at 2:21 PM. Reason: English correction

  9. #9
    Sencha User jack.slocum's Avatar
    Join Date
    Mar 2007
    Location
    Tampa, FL
    Posts
    6,955
    Vote Rating
    17
    jack.slocum will become famous soon enough jack.slocum will become famous soon enough

      0  

    Default


    {["pippo"], ["pluto"]}

    is not valid json or array data.

    As I think I showed above, it is probably easier to override the readRecords function than to subscribe to events. It will also net better performance and result in reusable code.

    That doesn't mean I am against events, I am a huge fan of events I just don't think they should be used in this case. Readers, by definition, are meant to handle different data formats so the definition of a new Reader for a new format is the intended way to do it.

    Where this gets interesting is in a future version of Ext where data conversion back to the original format will be supported as well (like Writers). Then the data formats used by the Readers/Writers will be more important.

    The TreeLoader is one of the most misunderstood classes in Ext. The only part of it's interface contract is the load(node, callback) method. It was specifically made simple to enable easy integration of additional formats. Here is a valid TreeLoader, created inline, which is backed by an Ext.data.Store:

    Code:
    var tree = new Ext.tree.TreePanel({
        ...
        loader:  {
            load: function(node, callback){
                var records = myStore.query('parentId', node.id);
                node.beginUpdate();
                for(var i = 0, len = records.length; i < len; i++){
                     node.appendChild(new Ext.tree.AsyncTreeNode(records[i].data));
                }
                node.endUpdate();
                callback();
            }
        }
        ...
    });
    Obviously this could be converted into a reusable class as well.
    Jack Slocum
    Ext JS Founder
    Original author of Ext JS 1, 2 & 3.
    Twitter: @jackslocum
    jack@extjs.com

  10. #10
    Sencha User
    Join Date
    Nov 2007
    Location
    London, UK
    Posts
    583
    Vote Rating
    -1
    mabello is an unknown quantity at this point

      0  

    Default


    Thanks again for your explanation Jack,

    I hope that I have not bother you too much !

    {["pippo"], ["pluto"]}, sorry about that

    I see your point of view about events.

    I agree with you about the TreeLoader, good tip!

    Anyway, I hope that someone will enjoy this discussion about DataReader, and I hope it will help someone else to have a better understanding about these important classes.

    Thanks for your time, hope to have another interesting discussion with you and thanks for your awesome work with Ext!

Thread Participants: 2