X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/6746dc89c47ed01b165cc1152533605f97eb8e8d..HEAD:/src/data/reader/Reader.js diff --git a/src/data/reader/Reader.js b/src/data/reader/Reader.js index aefce45e..4f4f6627 100644 --- a/src/data/reader/Reader.js +++ b/src/data/reader/Reader.js @@ -14,194 +14,207 @@ If you are unsure which license is appropriate for your use, please contact the */ /** * @author Ed Spencer - * @class Ext.data.reader.Reader - * @extends Object + * + * Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link + * Ext.data.Store Store} - often in response to an AJAX request. In general there is usually no need to create + * a Reader instance directly, since a Reader is almost always used together with a {@link Ext.data.proxy.Proxy Proxy}, + * and is configured using the Proxy's {@link Ext.data.proxy.Proxy#cfg-reader reader} configuration property: * - *

Readers are used to interpret data to be loaded into a {@link Ext.data.Model Model} instance or a {@link Ext.data.Store Store} - * - usually in response to an AJAX request. This is normally handled transparently by passing some configuration to either the - * {@link Ext.data.Model Model} or the {@link Ext.data.Store Store} in question - see their documentation for further details.

- * - *

Loading Nested Data

- * - *

Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.Association associations} - * configured on each Model. Below is an example demonstrating the flexibility of these associations in a fictional CRM system which - * manages a User, their Orders, OrderItems and Products. First we'll define the models: - * -


-Ext.define("User", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'name'
-    ],
-
-    hasMany: {model: 'Order', name: 'orders'},
-
-    proxy: {
-        type: 'rest',
-        url : 'users.json',
-        reader: {
-            type: 'json',
-            root: 'users'
-        }
-    }
-});
-
-Ext.define("Order", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'total'
-    ],
-
-    hasMany  : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'},
-    belongsTo: 'User'
-});
-
-Ext.define("OrderItem", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'price', 'quantity', 'order_id', 'product_id'
-    ],
-
-    belongsTo: ['Order', {model: 'Product', associationKey: 'product'}]
-});
-
-Ext.define("Product", {
-    extend: 'Ext.data.Model',
-    fields: [
-        'id', 'name'
-    ],
-
-    hasMany: 'OrderItem'
-});
-
- * - *

This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems. Finally, - * each OrderItem has a single Product. This allows us to consume data like this:

- * -

-{
-    "users": [
-        {
-            "id": 123,
-            "name": "Ed",
-            "orders": [
-                {
-                    "id": 50,
-                    "total": 100,
-                    "order_items": [
-                        {
-                            "id"      : 20,
-                            "price"   : 40,
-                            "quantity": 2,
-                            "product" : {
-                                "id": 1000,
-                                "name": "MacBook Pro"
-                            }
-                        },
-                        {
-                            "id"      : 21,
-                            "price"   : 20,
-                            "quantity": 3,
-                            "product" : {
-                                "id": 1001,
-                                "name": "iPhone"
-                            }
-                        }
-                    ]
-                }
-            ]
-        }
-    ]
-}
-
- * - *

The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the Orders - * for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case), and finally - * the Product associated with each OrderItem. Now we can read the data and use it as follows: - * -


-var store = new Ext.data.Store({
-    model: "User"
-});
-
-store.load({
-    callback: function() {
-        //the user that was loaded
-        var user = store.first();
-
-        console.log("Orders for " + user.get('name') + ":")
-
-        //iterate over the Orders for each User
-        user.orders().each(function(order) {
-            console.log("Order ID: " + order.getId() + ", which contains items:");
-
-            //iterate over the OrderItems for each Order
-            order.orderItems().each(function(orderItem) {
-                //we know that the Product data is already loaded, so we can use the synchronous getProduct
-                //usually, we would use the asynchronous version (see {@link Ext.data.BelongsToAssociation})
-                var product = orderItem.getProduct();
-
-                console.log(orderItem.get('quantity') + ' orders of ' + product.get('name'));
-            });
-        });
-    }
-});
-
- * - *

Running the code above results in the following:

- * -

-Orders for Ed:
-Order ID: 50, which contains items:
-2 orders of MacBook Pro
-3 orders of iPhone
-
+ * Ext.create('Ext.data.Store', { + * model: 'User', + * proxy: { + * type: 'ajax', + * url : 'users.json', + * reader: { + * type: 'json', + * root: 'users' + * } + * }, + * }); + * + * The above reader is configured to consume a JSON string that looks something like this: + * + * { + * "success": true, + * "users": [ + * { "name": "User 1" }, + * { "name": "User 2" } + * ] + * } * + * + * # Loading Nested Data + * + * Readers have the ability to automatically load deeply-nested data objects based on the {@link Ext.data.Association + * associations} configured on each Model. Below is an example demonstrating the flexibility of these associations in a + * fictional CRM system which manages a User, their Orders, OrderItems and Products. First we'll define the models: + * + * Ext.define("User", { + * extend: 'Ext.data.Model', + * fields: [ + * 'id', 'name' + * ], + * + * hasMany: {model: 'Order', name: 'orders'}, + * + * proxy: { + * type: 'rest', + * url : 'users.json', + * reader: { + * type: 'json', + * root: 'users' + * } + * } + * }); + * + * Ext.define("Order", { + * extend: 'Ext.data.Model', + * fields: [ + * 'id', 'total' + * ], + * + * hasMany : {model: 'OrderItem', name: 'orderItems', associationKey: 'order_items'}, + * belongsTo: 'User' + * }); + * + * Ext.define("OrderItem", { + * extend: 'Ext.data.Model', + * fields: [ + * 'id', 'price', 'quantity', 'order_id', 'product_id' + * ], + * + * belongsTo: ['Order', {model: 'Product', associationKey: 'product'}] + * }); + * + * Ext.define("Product", { + * extend: 'Ext.data.Model', + * fields: [ + * 'id', 'name' + * ], + * + * hasMany: 'OrderItem' + * }); + * + * This may be a lot to take in - basically a User has many Orders, each of which is composed of several OrderItems. + * Finally, each OrderItem has a single Product. This allows us to consume data like this: + * + * { + * "users": [ + * { + * "id": 123, + * "name": "Ed", + * "orders": [ + * { + * "id": 50, + * "total": 100, + * "order_items": [ + * { + * "id" : 20, + * "price" : 40, + * "quantity": 2, + * "product" : { + * "id": 1000, + * "name": "MacBook Pro" + * } + * }, + * { + * "id" : 21, + * "price" : 20, + * "quantity": 3, + * "product" : { + * "id": 1001, + * "name": "iPhone" + * } + * } + * ] + * } + * ] + * } + * ] + * } + * + * The JSON response is deeply nested - it returns all Users (in this case just 1 for simplicity's sake), all of the + * Orders for each User (again just 1 in this case), all of the OrderItems for each Order (2 order items in this case), + * and finally the Product associated with each OrderItem. Now we can read the data and use it as follows: + * + * var store = Ext.create('Ext.data.Store', { + * model: "User" + * }); + * + * store.load({ + * callback: function() { + * //the user that was loaded + * var user = store.first(); + * + * console.log("Orders for " + user.get('name') + ":") + * + * //iterate over the Orders for each User + * user.orders().each(function(order) { + * console.log("Order ID: " + order.getId() + ", which contains items:"); + * + * //iterate over the OrderItems for each Order + * order.orderItems().each(function(orderItem) { + * //we know that the Product data is already loaded, so we can use the synchronous getProduct + * //usually, we would use the asynchronous version (see {@link Ext.data.BelongsToAssociation}) + * var product = orderItem.getProduct(); + * + * console.log(orderItem.get('quantity') + ' orders of ' + product.get('name')); + * }); + * }); + * } + * }); + * + * Running the code above results in the following: + * + * Orders for Ed: + * Order ID: 50, which contains items: + * 2 orders of MacBook Pro + * 3 orders of iPhone */ Ext.define('Ext.data.reader.Reader', { requires: ['Ext.data.ResultSet'], alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'], /** - * @cfg {String} idProperty Name of the property within a row object - * that contains a record identifier value. Defaults to The id of the model. - * If an idProperty is explicitly specified it will override that of the one specified - * on the model + * @cfg {String} idProperty + * Name of the property within a row object that contains a record identifier value. Defaults to The id of the + * model. If an idProperty is explicitly specified it will override that of the one specified on the model */ /** - * @cfg {String} totalProperty Name of the property from which to - * retrieve the total number of records in the dataset. This is only needed - * if the whole dataset is not passed in one go, but is being paged from - * the remote server. Defaults to total. + * @cfg {String} totalProperty + * Name of the property from which to retrieve the total number of records in the dataset. This is only needed if + * the whole dataset is not passed in one go, but is being paged from the remote server. Defaults to total. */ totalProperty: 'total', /** - * @cfg {String} successProperty Name of the property from which to - * retrieve the success attribute. Defaults to success. See - * {@link Ext.data.proxy.Proxy}.{@link Ext.data.proxy.Proxy#exception exception} - * for additional information. + * @cfg {String} successProperty + * Name of the property from which to retrieve the success attribute. Defaults to success. See + * {@link Ext.data.proxy.Server}.{@link Ext.data.proxy.Server#exception exception} for additional information. */ successProperty: 'success', /** - * @cfg {String} root Required. The name of the property - * which contains the Array of row objects. Defaults to undefined. - * An exception will be thrown if the root property is undefined. The data - * packet value for this property should be an empty array to clear the data - * or show no data. + * @cfg {String} root + * The name of the property which contains the Array of row objects. For JSON reader it's dot-separated list + * of property names. For XML reader it's a CSS selector. For array reader it's not applicable. + * + * By default the natural root of the data will be used. The root Json array, the root XML element, or the array. + * + * The data packet value for this property should be an empty array to clear the data or show no data. */ root: '', /** - * @cfg {String} messageProperty The name of the property which contains a response message. - * This property is optional. + * @cfg {String} messageProperty + * The name of the property which contains a response message. This property is optional. */ /** - * @cfg {Boolean} implicitIncludes True to automatically parse models nested within other models in a response - * object. See the Ext.data.reader.Reader intro docs for full explanation. Defaults to true. + * @cfg {Boolean} implicitIncludes + * True to automatically parse models nested within other models in a response object. See the + * Ext.data.reader.Reader intro docs for full explanation. Defaults to true. */ implicitIncludes: true, @@ -260,10 +273,10 @@ Ext.define('Ext.data.reader.Reader', { }, /** - * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call - * this function before running its own logic and returning the Ext.data.ResultSet instance. For most - * Readers additional processing should not be needed. - * @param {Mixed} data The raw data object + * Abstracts common functionality used by all Reader subclasses. Each subclass is expected to call this function + * before running its own logic and returning the Ext.data.ResultSet instance. For most Readers additional + * processing should not be needed. + * @param {Object} data The raw data object * @return {Ext.data.ResultSet} A ResultSet object */ readRecords: function(data) { @@ -280,9 +293,8 @@ Ext.define('Ext.data.reader.Reader', { } /** + * @property {Object} rawData * The raw data object that was last passed to readRecords. Stored for further processing if needed - * @property rawData - * @type Mixed */ me.rawData = data; @@ -336,7 +348,7 @@ Ext.define('Ext.data.reader.Reader', { /** * Returns extracted, type-cast rows of data. Iterates to call #extractValues for each row - * @param {Object[]/Object} data-root from server response + * @param {Object[]/Object} root from server response * @private */ extractData : function(root) { @@ -376,7 +388,7 @@ Ext.define('Ext.data.reader.Reader', { * Loads a record's associations from the data object. This prepopulates hasMany and belongsTo associations * on the record provided. * @param {Ext.data.Model} record The record to load associations for - * @param {Mixed} data The data object + * @param {Object} data The data object * @return {String} Return value description */ readAssociated: function(record, data) { @@ -412,9 +424,9 @@ Ext.define('Ext.data.reader.Reader', { * Used internally by {@link #readAssociated}. Given a data object (which could be json, xml etc) for a specific * record, this should return the relevant part of that data for the given association name. This is only really * needed to support the XML Reader, which has to do a query to get the associated data object - * @param {Mixed} data The raw data object + * @param {Object} data The raw data object * @param {String} associationName The name of the association to get data for (uses associationKey if present) - * @return {Mixed} The root + * @return {Object} The root */ getAssociatedDataRoot: function(data, associationName) { return data[associationName]; @@ -464,15 +476,16 @@ Ext.define('Ext.data.reader.Reader', { * This will usually need to be implemented in a subclass. Given a generic data object (the type depends on the type * of data we are reading), this function should return the object as configured by the Reader's 'root' meta data config. * See XmlReader's getRoot implementation for an example. By default the same data object will simply be returned. - * @param {Mixed} data The data object - * @return {Mixed} The same data object + * @param {Object} data The data object + * @return {Object} The same data object */ getRoot: function(data) { return data; }, /** - * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be implemented by each subclass + * Takes a raw response object (as passed to this.read) and returns the useful data segment of it. This must be + * implemented by each subclass * @param {Object} response The responce object * @return {Object} The useful data from the response */ @@ -520,7 +533,7 @@ Ext.define('Ext.data.reader.Reader', { * @private * This builds optimized functions for retrieving record data and meta data from an object. * Subclasses may need to implement their own getRoot function. - * @param {Boolean} force True to automatically remove existing extractor functions first (defaults to false) + * @param {Boolean} [force=false] True to automatically remove existing extractor functions first */ buildExtractors: function(force) { var me = this,